Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android增量更新實現

Android增量更新實現

編輯:關於Android編程

Android增量更新技術在很多公司都在使用,網上也有一些相關的文章,但大家可能未必完全理解實現的方式,本篇博客,我將一步步的帶大家實現增量更新。

為什麼需要增量更新?

當我們開發完一個項目之後,上線維護 , 增加新功能 , 添加第三方庫 , APK大小從4 - 5M , 增長到10+M , 用戶由原來的幾十秒下載 , 到現在幾分鐘以上的下載 , 網絡情況不好的時候 , 或許就是十分鐘不等。每次全量下載 , 無論從體驗還是流量上 , 都是不友好的 , 所有增量更新還是有必要的 (小公司好像沒幾個用 , 一般大公司在用,如QQ空間)。

增量原理

這裡寫圖片描述
說白了,增量更新就是:用戶手機上安裝著某個應用,下載了增量包,手機上的apk和增量包合並形成新的包,然後再次安裝(注意這個過程是要重新安裝的,當然部分應用市場有root權限你可能感知不到)。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxzdHJvbmc+yrXP1su8wrejujwvc3Ryb25nPsq508O/qtS0z+7Ev2JzZGlmZiC9+NDQzsS8/sTayN2xyL3Po6yyosfSyrnTw8HLYnppcDK9+NDQzsS8/tG5y/Ugo6wgy/nT0LXDs/a1xLLut9aw/L/JxNyxyMDtwtvWtdKq0KEgo6wgvfjSu7K9v8nS1Lz1ydnTw7unwffBvyCho9T2wb+4/NDCIKOsIL3Pzqq52Lz8tcSyv7fWvs3Kx8n6s8my7rfWsPwgo6wgvavQwr7JQVBLvfjQ0LHIvc8go6wgyfqzydK7uPbQwrXEzsS8/iChozwvcD4NCjxoMSBpZD0="需要使用的資源">需要使用的資源

進行增加更新主要是通過開源項目bsdiff項目來進行實現,還需要一些輔助的工具,列表如下:
bsdiff — bsdiff (win環境) 生成差分包及合並差分包庫 , 源碼內已包含bzip2
項目地址:
https://github.com/hymanAndroid/tools/tree/master/bsdiff-4.3
bzip2 — bzip2 bsdiff 依賴
服務器 — Tomcat 7.0 (模擬網絡環境)放置差分包 , 供APP下載
開發工具 — Eclipse NDK開發 , 目前建議使用Eclipse開發
開發工具 — VS 因為服務器搭建在windows平台 , 所以使用VS開發JNI

如何進行差分包的生成?

下載完bsdiff之後 , 我們看到如下目錄:
這裡寫圖片描述
看這麼多文件 , 還有一些亂七八糟的不知道什麼的文件 , 那麼我們只關注 , 我們想要的文件 , 將C/C++源文件以及.h頭文件,找出來 ,放到一個干淨的文件夾中 。

1.使用visual studio去新建一個項目

這裡寫圖片描述

2.將bsdiff中的.c和.cpp文件放到VS項目源文件中,把.h文件放到VS項目頭文件中
這裡寫圖片描述
然後嘗試進行編譯,發現報錯,用到了過時的函數
這裡寫圖片描述

錯誤內容是:使用了非安全的函數 , 解決方法是在文件中聲明#define _CRT_SECURE_NO_WARNINGS即可 。如果是多個文件都需要進行聲明,顯然一條條的聲明太麻煩了,所以可以在屬性中聲明,所有的文件都適用。

這裡寫圖片描述

修改完畢,再次嘗試編譯,還是報錯,內容如下:
這裡寫圖片描述

原因是在VS中通不過安全語法檢查 , 在VS中進行如下設置,將SDL檢查由“是”改為“否”:

這裡寫圖片描述

還需要在文件中添加#define _CRT_NONSTDC_NO_DEPRECATE預處理指令。
這裡寫圖片描述

然後我們需要生成.dll動態庫,需要我們在配置中,將“配置類型”改為“動態庫(.dll)”

這裡寫圖片描述

編譯生成.dll動態庫後 , 賦值到服務器項目的目錄下 , 或是Java項目也可以 。
編譯生成.dll動態庫時,還可能出現一個坑,那就是:變量未初始化

    u_char *old = nullptr,*_new = nullptr;
    off_t oldsize,newsize;
    off_t *I,*V = nullptr;

可能會是這幾個 , 將其賦值為nullptr就可以了。 如果還有其他錯誤 , 可執行分析 , google , 也歡迎評論留言 , 多多交流 。

3.了解bsdiff的用法 , 在main函數中尋找思路

int main(int argc,char *argv[])
{
    int fd;
    u_char *old,*_new;
    off_t oldsize,newsize;
    off_t *I,*V;
    off_t scan,pos,len;
    off_t lastscan,lastpos,lastoffset;
    off_t oldscore,scsc;
    off_t s,Sf,lenf,Sb,lenb;
    off_t overlap,Ss,lens;
    off_t i;
    off_t dblen,eblen;
    u_char *db,*eb;
    u_char buf[8];
    u_char header[32];
    FILE * pf;
    BZFILE * pfbz2;
    int bz2err;

    if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);

在bsdiff.cpp文件找到帶參數的main函數 , 並且有一個關於用法的線索 , 那就是:

if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);

我們可以根據這句話來推測 , 需要四個參數 , 並且三個參數必須傳入的文件路徑 , 剩下的一個參數不會有影響,直接傳入任何值都可以。

4.創建JNI方法 , 修改main函數
既然知道了需要傳入的參數 , 那麼就可以創建一個Java工程 , 編寫JNI方法了。

public class BsDiff {

    /**
     * 生成差分包
     * 
     * @param oldfile 老版本文件路徑
     * @param newfile 新版本文件路徑
     * @param patchfile 生成差分包文件路徑
     */
    public native static void diff(String oldfile, String newfile, String patchfile);

    static{
        System.loadLibrary("bsdiff");
    }
}

接下來使用javah命令,生成頭文件 , 將.h頭文件拷貝到我們 VS的工程中,同時com_dispatch_bsdiff_BsDiff.h頭文件還需要依賴jni.h和jni_md.h兩個文件

這裡寫圖片描述

com_dispatch_bsdiff_BsDiff.h中引入jni.h
這裡寫圖片描述

將頭文件com_dispatch_bsdiff_BsDiff.h引入到bsdiff.cpp文件中

這裡寫圖片描述

5.編寫調用的C函數 , 並修改main函數名稱,main函數作為入口函數 , 在JNI中就適用了 , 所有將main函數名稱改一下 , 在JNI的C函數中調用即可 。
這裡寫圖片描述

編寫bsdiff.cpp的native函數

//JNI 調用
JNIEXPORT void JNICALL Java_com_dispatch_bsdiff_BsDiff_diff
(JNIEnv *env, jclass jcls, jstring oldfile_jstr, jstring newfile_jstr, jstring patchfile_jstr){
    int argc = 4;
    char* oldfile = (char*)env->GetStringUTFChars(oldfile_jstr, NULL);
    char* newfile = (char*)env->GetStringUTFChars(newfile_jstr, NULL);
    char* patchfile = (char*)env->GetStringUTFChars(patchfile_jstr, NULL);

    //參數(第一個參數無效)
    char *argv[4];
    argv[0] = "bsdiff";
    argv[1] = oldfile;
    argv[2] = newfile;
    argv[3] = patchfile;

    bsdiff_main(argc, argv);

    env->ReleaseStringUTFChars(oldfile_jstr, oldfile);
    env->ReleaseStringUTFChars(newfile_jstr, newfile);
    env->ReleaseStringUTFChars(patchfile_jstr, patchfile);
}


一切修改完畢 ,重新編譯生成.dll動態庫 。
差分包工具類:

public class BsdiffUtils {

        /**
         * 生成差分包
         * @param oldFilePath 老版本文件路徑
         * @param newFilePath 新版本文件路徑
         * @param patchFilePath 生成差分包文件路徑
         */
        public static native void diff(String oldFilePath , String newFilePath , String patchFilePath) ;



}

使用差分包工具類:

public class BsdiffApk {

    public static void main(String args[]) {

        /**
         * 文件差分
         */
        BsdiffUtils.diff(Constract.OLD_FILE_PATH, Constract.NEW_FILE_PATH, Constract.PATCH_FILE_PATH);
    }
}

常量類,主要用於存儲差分包路徑:

public class Constract {

    public static final String OLD_FILE_PATH = "E:/javaee_workspace/AppUpdateServer/apk/app-old.apk" ;
    public static final String NEW_FILE_PATH = "E:/javaee_workspace/AppUpdateServer/apk/app-new.apk" ;
    public static final String PATCH_FILE_PATH = "E:/javaee_workspace/AppUpdateServer/apk/App_patch.patch" ;
}

生成差分包:
這裡寫圖片描述

合並差分包

1.提取bzip2中的源文件
這裡寫圖片描述

2.將bzip2加入到Android Studio項目中
首先將工程切換到Project模式 , 將bzip2文件夾復制到cpp目錄下 。因為最新的Android Studio采用的是CMake構建工具 , 所有需要在bzip2目錄下,創建一個CMakeLists.txt文件:
這裡寫圖片描述

3.將bspatch.c復制到cpp目錄下 , 並將自動生成的CMakeList.txt文件拖拽到cpp目錄下 , 並添加子目錄參與編譯 。

這裡寫圖片描述

修改了CMakeLists.txt文件的路徑之後 , 需要在build.gradle中修改一下配置了:

這裡寫圖片描述

並且配置一下build環境
這裡寫圖片描述

4.然後就是在java代碼中編寫合並差分文件的JNI方法

public class BspatchJNI {

    /**
     * 合並增量文件
     * @param oldFilePath 當前APK路徑
     * @param newFilePath 合成後的新的APK路徑
     * @param patchFilePath 增量文件路徑
     */
    public static native void bspatchJNI(String oldFilePath,String newFilePath,String patchFilePath) ;

    static {
        System.loadLibrary("bspatch");
    }
}

5.編寫C函數 , 怎樣找執行函數 , 和前面拆分文件的套路是一樣的

/*合並APK*/
JNIEXPORT void JNICALL
Java_com_zeno_incrementupdate_ndk_BspatchJNI_bspatchJNI(JNIEnv *env, jclass type,
                                                        jstring oldFilePath_, jstring newFilePath_,
                                                        jstring patchFilePath_) {
    const char *oldFilePath = (*env)->GetStringUTFChars(env, oldFilePath_, 0);
    const char *newFilePath = (*env)->GetStringUTFChars(env, newFilePath_, 0);
    const char *patchFilePath = (*env)->GetStringUTFChars(env, patchFilePath_, 0);


    // if(argc!=4) errx(1,"usage: %s oldfile newfile patchfile\n",argv[0]);

    int argc = 4 ;
    char* argv[4] ;
    argv[0] = "bspatch";
    argv[1] = oldFilePath;
    argv[2] = newFilePath;
    argv[3] = patchFilePath;

    bspatch_main(argc,argv);

    LOGE("MainActivity","%s","合並APK完成");

    (*env)->ReleaseStringUTFChars(env, oldFilePath_, oldFilePath);
    (*env)->ReleaseStringUTFChars(env, newFilePath_, newFilePath);
    (*env)->ReleaseStringUTFChars(env, patchFilePath_, patchFilePath);
}

需要注意的時 , 在bspatch.c中是需要引入bzip2的 , 所有需要在文件頭部, 引入bzip2 :

/ bzip2
#include "bzip2/bzlib.c"
#include "bzip2/crctable.c"
#include "bzip2/compress.c"
#include "bzip2/decompress.c"
#include "bzip2/randtable.c"
#include "bzip2/blocksort.c"
#include "bzip2/huffman.c"

#define LOGE(TAG,FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,TAG,FORMAT,__VA_ARGS__)

拆分和合並的代碼就已經寫完了,其余下載的java代碼,大家可以根據自己項目的情況去進行編寫,這裡就不再貼出

6.打包
因為Android Studio使用了instant run技術 , 所以使用Android Studio生成APK最好是打正式包 , 並且包中內容要有差異性 , 然後再生成差分包 , 直接放置在WEB項目的WebContent根目錄下即可 。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved