Android 累加式更新完全解析 是增量不是熱修複

來源:互聯網
上載者:User

標籤:儲存   detail   ack   tabs   keyword   ber   tar.gz   sep   strong   

轉自:http://blog.csdn.net/lmj623565791/article/details/52761658

本文在我的公眾號:鴻洋(hongyangAndroid)首發。

轉載請標明出處: 
http://blog.csdn.net/lmj623565791/article/details/52761658; 
本文出自:【張鴻洋的部落格】

一、概述

最近一直關注熱修複的東西,偶爾聊天談到了累加式更新,當然了兩個完全不是一個東西。藉此找了一些資料,收集整理了一下,本來是不想寫部落格的,因為主要都是工具的實現,但是昨晚在整理資料的時候,忽然發現,我快要忘了這玩意,又要從頭找一圈工具。

So,權當一個記錄,也方便以後自己尋找。

首先要明確的是,什麼是累加式更新:

相信大家都見過在應用市場省流量更新軟體,一個幾百M的軟體可能只需要下載一個20M的增量包就能完成更新。那麼它是如何做的呢?

就是本篇部落格的主題了。

累加式更新的流程是:使用者手機上安裝著某個應用,下載了增量包,手機上的apk和增量包合并形成新的包,然後再次安裝(注意這個過程是要重新安裝的,當然部分應用市場有root許可權你可能感知不到)。

ok,那麼把整個流程細化為幾個關鍵點:

  1. 使用者手機上提取當前安裝應用的apk
  2. 如何利用old.apk和new.apk產生增量檔案
  3. 增加檔案與1.中的old.apk合并,然後安裝

解決了上述3個問題,就ok了。

下面開始解決,首先我們看下增量檔案的產生與合并,這個環節可以說是整個流程的核心,也是技術痛點,值得開心的是,這個技術痛點已經有工具替我們實現了。

二、增量檔案的產生與合并

這個其實就是利用工具做二進位的一個diff和patch了。

網址:

  • http://www.daemonology.net/bsdiff/

  • http://www.daemonology.net/bsdiff/bsdiff-4.3.tar.gz

對了,本文環境為mac,其他系統如果阻礙,慢慢搜尋解決即可。

下載好了,解壓,切到對應的目錄,然後執行make:

aaa:bsdiff-4.3 zhy$ makeMakefile:13: *** missing separator.  Stop.
  • 1
  • 2

恩,你沒看錯,報錯了,這個錯誤還比較好解決。

解壓檔案裡面有個檔案:Makefile,以文本的形式開啟,將install:下面的if,endif添加一個縮排。

修改完成是這個樣子的:

CFLAGS      +=  -O3 -lbz2PREFIX      ?=  /usr/localINSTALL_PROGRAM ?=  ${INSTALL} -c -s -m 555INSTALL_MAN ?=  ${INSTALL} -c -m 444all:        bsdiff bspatchbsdiff:     bsdiff.cbspatch:    bspatch.cinstall:    ${INSTALL_PROGRAM} bsdiff bspatch ${PREFIX}/bin    .ifndef WITHOUT_MAN    ${INSTALL_MAN} bsdiff.1 bspatch.1 ${PREFIX}/man/man1    .endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

然後,重新執行make:

aaa:bsdiff-4.3 zhy$ makecc -O3 -lbz2    bsdiff.c   -o bsdiffcc -O3 -lbz2    bspatch.c   -o bspatchbspatch.c:39:21: error: unknown type name ‘u_char‘; did you mean ‘char‘?static off_t offtin(u_char *buf)                    ^~~~~~                    char
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

這次比上次好點,這次產生了一個bsdiff,不過在產生bspatch的時候報錯了,好在其實我們只需要使用bsdiff,為什麼這麼說呢?

因為產生增量檔案肯定是在服務端,或者是我們本地pc上做的,使用的就是bsdiff這個工具;

另外一個bspatch,合并old.apk和增量檔案肯定是在我們應用內部做的。

當然這個問題也是可以解決的,搜尋下,很多解決方案,我們這裡就不繼續在這個上面浪費篇幅了。

我這裡提供個:

https://github.com/hymanAndroid/tools/tree/master/bsdiff-4.3

下載完成,直接make,bsdiff和bspatch都會產生(mac環境下)。

=============神奇的分割線==============

ok,假設到這裡,不管你使用何種手段,咱們已經有了bsdiff和bspacth,下面示範下這個工具的使用:

首先我們準備兩個apk,old.apk和new.apk,你可以自己隨便寫個項目,先運行一次拿到產生的apk作為old.apk;然後修改些代碼,或者加一些功能,再運行一次產生new.apk;

  • 產生增量檔案
./bsdiff old.apk new.apk old-to-new.patch
  • 1

這樣就產生了一個增量檔案old-to-new.patch

  • 增量檔案和old.apk合并成新的apk
./bspatch old.apk new2.apk old-to-new.patch
  • 1

這樣就產生一個new2.apk

那麼怎麼證明這個產生的new2.apk和我們的new.apk一模一樣呢?

我們可以查看下md5的值,如果兩個檔案md5值一致,那麼幾乎可以肯定兩個檔案時一模一樣的(不要跟我較真說什麼碰撞可以產生一樣的md5的值~~)。

aaa:bsdiff-4.3 zhy$ md5 new.apk MD5 (new.apk) = 0900d0d65f49a0cc3b472e14da11bde7aaa:bsdiff-4.3 zhy$ md5 new2.apk MD5 (new2.apk) = 0900d0d65f49a0cc3b472e14da11bde7
  • 1
  • 2
  • 3
  • 4

可以看到兩個檔案的md5果然一樣~~

恩,假設你不是mac,怎麼擷取一個檔案的md5呢?(自己寫代碼,下載工具,不要遇到這樣的問題,還彈窗我,我會被扣工資的…)

那麼到這裡我們就已經知道了如何產生增量檔案和將patch與舊的檔案合并為新的檔案。那麼我們再次梳理下整個流程:

  1. 服務端已經做好了增量檔案(本節完成)
  2. 用戶端下載增量檔案+提取該應用的apk,使用bspatch合并
  3. 產生的新的apk,調用安裝程式

還是蠻清晰的,那麼主要是第二點,第二點有兩件事,一個是提取應用的apk;一個是使用bspatch合并,那麼這個合并肯定是需要native方法和so檔案去做的,也就是說我們要自己打個so出來;

三、用戶端的行為(1)提取應用的apk檔案

其實提取當前應用的apk非常簡單,如下代碼:

public class ApkExtract {    public static String extract(Context context) {        context = context.getApplicationContext();        ApplicationInfo applicationInfo = context.getApplicationInfo();        String apkPath = applicationInfo.sourceDir;        Log.d("hongyang", apkPath);        return apkPath;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
(2)製作bspatch so

首先聲明一個類,寫個native方法,如下:

public class BsPatch {    static {        System.loadLibrary("bsdiff");    }    public static native int bspatch(String oldApk, String newApk, String patch);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

三個參數已經很明確了;

同時別忘了在module的build.gradle下面:

defaultConfig {    ndk {        moduleName = ‘bsdiff‘    }}
  • 1
  • 2
  • 3
  • 4
  • 5

注意該步驟需要你配置過ndk的環境(下載ndk,設定ndk.dir)~

ok,接下來就是去完成c的代碼的編寫了;

首先在app/main目錄下建立一個檔案夾jni,把之前下載的bsdiff中的bspatch.c拷貝進去;

然後按照jni的規則,在裡面建立一個方法:

JNIEXPORT jint JNICALL Java_com_zhy_utils_BsPatch_bspatch        (JNIEnv *env, jclass cls,         jstring old, jstring new, jstring patch){    int argc = 4;    char * argv[argc];    argv[0] = "bspatch";    argv[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));    argv[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));    argv[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));    int ret = patchMethod(argc, argv);    (*env)->ReleaseStringUTFChars(env, old, argv[1]);    (*env)->ReleaseStringUTFChars(env, new, argv[2]);    (*env)->ReleaseStringUTFChars(env, patch, argv[3]);    return ret;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

方法名是有規律的,這個規律不用提了吧~~

注意bsdiff.c中並沒有patchMethod方法,這個方法實際上是main方法,直接修改為patchMethod即可,覺得複雜沒關係,文末有源碼。

ok,此時你可以嘗試運行,會提示依賴bzlib,其實從檔案頂部的include中也能看出來。

既然依賴,那我們就匯入吧:

首先下載:

  • http://www.bzip.org/downloads.html
  • http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz

下載完成後,解壓:

將其中的.h和.c檔案提取出來,然後可以選擇連檔案夾copy到我們module的app/main/jni下,結果如下:

記得修改bsdiff中的include:

#include "bzip2/bzlib.h"
  • 1

再次運行;

然後會發現報一堆類似下面的錯誤:

Error:(70) multiple definition of `main‘
  • 1

提示main方法重複定義了,在出錯資訊中會給出哪些類中包含main方法,可以選擇直接將這些類中的main方法直接刪除。

刪除以後,就ok了~~

那麼到這裡,我們就完成了JNI的編寫,當然檔案是bsdiff提供的c源碼。

四、累加式更新後安裝

上面的操作完成後,最後一步就簡單了,首先準備兩個apk:

old.apk new.apk
  • 1

然後製作一個patch,下面代碼中的PATCH.patch;

將old.apk安裝,然後將new.apk以及PATCH.patch放置到儲存卡;

最後在Activity中觸發調用:

private void doBspatch() {    final File destApk = new File(Environment.getExternalStorageDirectory(), "dest.apk");    final File patch = new File(Environment.getExternalStorageDirectory(), "PATCH.patch");    //一定要檢查檔案都存在    BsPatch.bspatch(ApkExtract.extract(this),            destApk.getAbsolutePath(),            patch.getAbsolutePath());    if (destApk.exists())        ApkExtract.install(this, destApk.getAbsolutePath());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

記得開啟讀寫SDCard許可權,記得在代碼中校正需要的檔案都存在。

install實際就是通過Intent去安裝了:

 public static void install(Context context, String apkPath) {        Intent i = new Intent(Intent.ACTION_VIEW);        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        i.setDataAndType(Uri.fromFile(new File(apkPath)),                "application/vnd.android.package-archive");        context.startActivity(i);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

這裡7.0可能會有問題,把路徑暴露給別的app了,應該需要FileProvider去實現(未實驗,猜測可能有可能)。

大致的如下:

五、總結

如果你只是單純的要使用該功能,大可以直接將產生的so檔案拷入,直接loadLibrary使用即可。

其次,在做累加式更新的時候,patch肯定是根據你當前的版本號碼與最新(或者目標)版本apk,比對下發diff檔案,於此同時應該也把目標apk的md5下發,再做完合并後,不要忘記校正下md5;

部落格結束,雖然很簡單,主要利用工具實現,但是還是建議自己去實現一次,想一次性跑通還是需要一些時間的,可能過程中也會發現一些坑,也能提升自己對JNI的熟練度。

源碼:

  • https://github.com/hongyangAndroid/BsDiff_And_Patch

也可以選擇直接使用so

  • https://github.com/hongyangAndroid/BsDiff_And_Patch/tree/master/so-dist

歡迎關注我的微博: 
http://weibo.com/u/3165018720

Android 累加式更新完全解析 是增量不是熱修複(轉)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.