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

android增量更新

編輯:關於Android編程

今天學習了一下增量更新,這個技術已經出現很長時間了,但是現實中,估計只有大廠才利用了這一技術在做產品!
國內有些第三方服務平台,像友盟提供自動更新的服務,也是用的增量方式!
其他的像QQ,Sina微博,陌陌,蘑菇街等都用到了,解壓一下它們的apk,看一下lib目錄:
QQ:libbspatch.so
微博:libbsdiffjni.so
陌陌:libbsdiff.so
蘑菇街:libpatcher.so
這些只不過so的名字不一樣而已,但都用到了增量更新.
其它的一些主流app解包後,都看到了libandfix.so這個庫,這個是阿裡推出的一個支持ART和Dalvik的熱修復的框架,在線修復bug.

今天學習增量更新而非熱更新
主角:http://www.daemonology.net/bsdiff
官方說明:
(1)bsdiff 和 bspatch是編譯,安裝補丁到二進制文件的一個工具,
(2)這個工具用到了bzip2的壓縮功能,所以補丁的大小小於新舊版本的一個差值
(3)bsdiff非常吃內存

1.編譯生成工具

開干,我用的是ubuntu,點擊here下源代碼,編譯
\
解壓,可以看到一共5個文件<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> shone@Dell:~/Soft/bsdiff-4.3$ ls bsdiff.1 bsdiff.c bspatch.1 bspatch.c Makefile

然後編譯make,報錯

Makefile:13: *** missing separator.  Stop.

查看一下Makefile
\
無奈,在網上找了一下,找到了解決方法.
http://kinggoo.com/bsdiffupdate.htm
原因是:目標體下一行的,命令要用TAB鍵開頭,且不能隔一行。
也就是說在.ifndef的前面要有TAB開頭才可以~因為他是安裝的下一個子集命令。
\

接著編譯,結果報錯,找不到頭文件

bsdiff.c:33:19: fatal error: bzlib.h: No such file or directory
 #include 
                   ^
compilation terminated.

先前說過bsdiff依賴bzip2,有圖
\
好,下載bzip2,解壓
http://www.bzip.org/downloads.html
make
sudo make install
安裝的時候,會創建一些文件,所以給權限
然後編譯

cc -O3  -lbz2    bsdiff.c   -o bsdiff
/tmp/cctTxPKV.o: In function `main':
bsdiff.c:(.text.startup+0x2aa): undefined reference to `BZ2_bzWriteOpen'
bsdiff.c:(.text.startup+0x9e9): undefined reference to `BZ2_bzWrite'
bsdiff.c:(.text.startup+0xb2c): undefined reference to `BZ2_bzWrite'
bsdiff.c:(.text.startup+0xc7b): undefined reference to `BZ2_bzWrite'
bsdiff.c:(.text.startup+0xccf): undefined reference to `BZ2_bzWriteClose'
bsdiff.c:(.text.startup+0xd22): undefined reference to `BZ2_bzWriteOpen'
bsdiff.c:(.text.startup+0xd4d): undefined reference to `BZ2_bzWrite'
bsdiff.c:(.text.startup+0xd73): undefined reference to `BZ2_bzWriteClose'
bsdiff.c:(.text.startup+0xdc6): undefined reference to `BZ2_bzWriteOpen'
bsdiff.c:(.text.startup+0xdf1): undefined reference to `BZ2_bzWrite'
bsdiff.c:(.text.startup+0xe17): undefined reference to `BZ2_bzWriteClose'
collect2: error: ld returned 1 exit status
make: *** [bsdiff] Error 1

第一個反應還是,連接bzip2庫除了問題,其實不用makefile腳本也可以得到2個二進制工具

CFLAGS          +=      -O3 -lbz2

PREFIX          ?=      /usr/local
INSTALL_PROGRAM ?=      ${INSTALL} -c -s -m 555
INSTALL_MAN     ?=      ${INSTALL} -c -m 444

all:            bsdiff bspatch
bsdiff:         bsdiff.c
bspatch:        bspatch.c

install:
        ${INSTALL_PROGRAM} bsdiff bspatch ${PREFIX}/bin
        .ifndef WITHOUT_MAN
        ${INSTALL_MAN} bsdiff.1 bspatch.1 ${PREFIX}/man/man1
        .endif

仔細看下這個Makefile, bsdiff和bspatch分別是2個模塊,互不依賴,用make僅僅是多了可以指定目標到/usr/local目錄,然後提供了一鍵安裝功能,就是部署二進制工具和幫助(man)命令到/usr/local, Makefile僅僅是大項目管理的利器,還好這個文件不多,可以2次手動編譯就完成了!

gcc bsdiff.c -lbz2 -o bsdiff

好可以生成,bzip2庫之前已經安裝在系統標准目錄

gcc bspatch.c -lbz2 -o bspatch

2個工具都可以正常生成!

linux下鏈接庫通常會去3個文件下找
/lib是內核級的,/usr/lib是系統級的,/usr/local/lib是用戶級的

想了下,Makefile估計還是有點問題,然後自己改了下

CC=gcc

LDFLAGS=

CFLAGS=-Wall -O3 -g -lbz2


PREFIX          ?=      /usr/local
INSTALL_PROGRAM ?=      ${INSTALL} -c -s -m 555
INSTALL_MAN     ?=      ${INSTALL} -c -m 444


all:            bsdiff bspatch
bsdiff:         bsdiff.c
        $(CC) bsdiff.c  $(CFLAGS) $(LDFLAGS) -o  bsdiff
bspatch:        bspatch.c
        $(CC) bspatch.c  $(CFLAGS) $(LDFLAGS) -o  bspatch


install:
        ${INSTALL_PROGRAM} bsdiff bspatch ${PREFIX}/bin
        .ifndef WITHOUT_MAN
        ${INSTALL_MAN} bsdiff.1 bspatch.1 ${PREFIX}/man/man1
        .endif

這樣make就可以成功生成2個工具了
\

Makefile的規則
  目標 : 需要的條件 (注意冒號兩邊有空格)
    命令  (注意前面用tab鍵開頭)
  解釋一下:
  1 目標可以是一個或多個,可以是Object File,也可以是執行文件,甚至可以是一個標簽。
  2 需要的條件就是生成目標所需要的文件或目標
  3 命令就是生成目標所需要執行的腳本

2.生成補丁文件

差分生成補丁方式
bsdiff old_version.apk new_version.apk diff.patch
分別是3個參數,老版本,新版本,補丁
還沒有apk,首要任務先生成2個版本的apk
這裡要把補丁合成功能移植到手機客戶端上,那麼就要用到NDK了

bsdiff是二進制差分工具,其對應的bspatch是相應的補丁合成工具

鏡頭切換到androidstudio上

2.1 先定義一個調度native層的類

public class Updater {

    public static native void applyPatch(String oldPath, String newPath, String patchPath);

}

2.2 生成.h頭文件

因為as的版本原因,生成.h的方式也不一樣,我用的androidstudio版本是2.1.1v,網上方法大多不可行,正確的姿勢是

shone@Dell:~/Public/work_androidstudio/PatchUpdate/app/build/intermediates/classes/debug$ javah com.sugar.patch.Updater

主要是先編譯一下模塊,然後進入到debug目錄,用javah命令
好了,如果沒有問題,會看見一個.h文件
\

2.3 編寫補丁合成代碼

<1>首先建立一個jni的文件夾,將.h頭文件移動過來
<2>因為補丁合成用到bzip2解壓,所以要把zip2的包拷貝過來
<3>然後將bspatch.c拷貝到jni下面
bspatch.c實際上只有2個函數,一個offtin和main(),增加一個native函數

JNIEXPORT void JNICALL Java_com_sugar_patch_Updater_applyPatch
  (JNIEnv *env, jclass clazz, jstring old_path, jstring new_path, jstring patch){
      int argc=4;
      char * argv[argc];
      argv[0]="bspatch";
      argv[1]=(*env)->GetStringUTFChars(env, old_path, 0);
      argv[2]=(*env)->GetStringUTFChars(env, new_path, 0);
      argv[3]=(*env)->GetStringUTFChars(env, patch, 0);

      int ret = domain(argc, argv);

       (*env)->ReleaseStringUTFChars(env,old_path, argv[1]);
       (*env)->ReleaseStringUTFChars(env,new_path, argv[2]);
       (*env)->ReleaseStringUTFChars(env,patch, argv[3]);
       return ret;
}

懂jni的都知道,這個函數名是不能亂寫的,必須把.h頭文件的那個聲明函數拷貝過來,然後參數修改和增加主體,java調native會傳值到這個函數裡來!
然後這裡會調用main()方法,這裡我把main方法名字改成了domain,因為main是C程序的入口,這裡不需要這個入口
下面是我自己的測試案例:
\

native代碼寫好了,那麼java需要傳入3個參數的值

java核心代碼
String oldVersionPath = AppUtils.getOldVersionPath(context.get());
Updater.applyPatch(oldVersionPath, newVersionPath, downPatchPath);

這裡需要三個路徑,定義常量

public interface Contants {

    String rootDir = Environment.getExternalStorageDirectory().getAbsolutePath()
            + File.separator + "PatchCache" + File.separator;
    String downPatchPath = rootDir + "apk.patch";
    String newVersionPath = rootDir + "PatchUpdate_v_2_0.apk";
}

oldVersionPath為app在手機上的安裝包路徑,從ApplicationInfo這個類獲得,newVersionPath為新版本apk包合成地址,downPatchPath為補丁地址

為了測試,這裡把補丁文件apk.patch會先放到sd卡/PatchCache/這個目錄下,等app合成完成後,新的apk會輸出到sd卡/PatchCache/下
\

然後有了合成後的,就可以安裝更新了

AppUtils.install(context.get(), newVersionPath);

服務端差分補丁

shone@Dell:~/Soft/bsdiff-4.3$ ls
app_v_1_0.apk  bsdiff    bsdiff.c  bspatch.1  Makefile
app_v_2_0.apk  bsdiff.1  bspatch   bspatch.c

app_v_1_0.apk和app_v_2_0.apk分別是1.0和2.0版本,測試的話
我下了幾張高清圖片放到了2.0版本的assets下面,這樣2.0版本的apk大小會比較大,1.0版本大小是1.7M,2.0版本是16.9M,差分後apk.patch有14M!

shone@Dell:~/Soft/bsdiff-4.3$ ./bsdiff app_v_1_0.apk app_v_2_0.apk apk.patch
shone@Dell:~/Soft/bsdiff-4.3$ ls
apk.patch  app_v_1_0.apk  app_v_2_0.apk  bsdiff  bsdiff.1  bsdiff.c  bspatch  bspatch.1  bspatch.c  Makefile

好了,分析到這了
下面是我的測試項目,可以自己測測
https://github.com/shonegg/PatchUpdate

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