Android増分更新完全解析増分か熱修復か

11928 ワード

一、概説
最近ずっと熱修復のものに注目して、たまにチャットして増分更新について話して、もちろん2つはまったく1つのものではありません.これを借りていくつかの資料を探して、収集して整理して、もともとブログを書きたくないので、主にツールの実現なので、しかし昨夜資料を整理する時、突然発見して、私はこのものを忘れそうで、また最初からツールを探します.
Soは、記録になる権利があり、後で自分で探すのも便利です.
まず、インクリメンタル更新とは何かを明確にします.
アプリケーション市場で省トラフィック更新ソフトウェアを見たことがあると思います.数百Mのソフトウェアは20 Mのインクリメンタルパッケージをダウンロードするだけで更新が完了する可能性があります.どうやって作ったのでしょうか?
このブログのテーマです.
インクリメンタル更新の流れは、ユーザーの携帯電話にアプリケーションがインストールされ、インクリメンタルパッケージがダウンロードされ、携帯電話のapkとインクリメンタルパッケージが統合されて新しいパッケージが形成され、再インストールされます(このプロセスは再インストールされることに注意してください.もちろん、一部のアプリケーション市場にはroot権限があるので、感知できない可能性があります).
OKでは、プロセス全体をいくつかのキーに細分化します.
  • ユーザーの携帯電話から現在インストールされているアプリケーションのapk
  • を抽出する
  • oldの利用方法Apkとnew.apk生成インクリメンタルファイル
  • ファイルと1.に表示されます.apkをマージし、
  • をインストールします.
    上記3つの問題を解決したらOKです.
    次に解決し始めます.まず、インクリメンタルファイルの生成と統合を見てみましょう.この一環はプロセス全体の核心と言えるし、技術的な難点でもあります.嬉しいことに、この技術的な難点はすでに私たちのために実現されています.
    二、増分ファイルの生成と合併
    これは実はツールを利用してバイナリを作るdiffとpatchです.
    Webサイト:
  • www.daemonology.net/bsdiff/

  • ダウンロード先:
  • www.daemonology.net/bsdiff/bsdi…

  • ところで,本稿の環境はmacであり,他のシステムが阻害すれば,ゆっくり探索して解決すればよい.
    ダウンロードして、解凍して、対応するディレクトリにカットして、makeを実行します.
    aaa:bsdiff-4.3 zhy$ make
    Makefile:13: *** missing separator.  Stop.

    はい、あなたは間違っていません.新聞を間違えました.この間違いはまだ解決しやすいです.
    解凍ファイルの中にファイルがあります:Makefile、テキストの形式で開いて、install:下のif、endifをインデントします.
    修正が完了すると、次のようになります.
    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

    次に、makeを再実行します.
    aaa:bsdiff-4.3 zhy$ make
    cc -O3 -lbz2    bsdiff.c   -o bsdiff
    cc -O3 -lbz2    bspatch.c   -o bspatch
    bspatch.c:39:21: error: unknown type name 'u_char'; did you mean 'char'?
    static off_t offtin(u_char *buf)
                        ^~~~~~
                        char

    今回は前回より少し良くて、今回はbsdiffを生成しましたが、bspatchを生成する時に間違っていました.幸いにも私たちはbsdiffを使うだけです.どうしてそう言いますか.
    インクリメンタルファイルの生成はサービス側、またはローカルpc上で行われるに違いないので、bsdiffというツールを使用しています.もう1つのbspatchはoldを合併する.apkとインクリメンタルファイルは、私たちのアプリケーションの内部で作られたに違いありません.
    もちろんこの問題も解決できます.検索してみると、多くの解決策があります.私たちはここでこの上で紙幅を浪費し続けません.
    ここにダウンロードアドレスを提供します.
    github.com/hymanAndroi…
    ダウンロードが完了すると、直接make、bsdiff、bspatchが生成されます(mac環境下).
    ============不思議な分割線==========
    OK、ここまで仮定すると、どんな手段を使っても、bsdiffとbspacthがあります.次に、このツールの使用を説明します.
    まず2つのapkを用意しますApkとnew.apk、自分で勝手にプロジェクトを書いて、まず生成したapkをoldとして実行することができます.apk;その後、コードを修正したり、機能を追加したりして、newの生成をもう一度実行します.apk;
  • インクリメンタルファイル
  • を生成する.
    ./bsdiff old.apk new.apk old-to-new.patch

    これにより、インクリメンタルファイルold-to-newが生成する.patch
  • インクリメンタルファイルとold.apkを新しいapk
  • に統合
    ./bspatch old.apk new2.apk old-to-new.patch

    これによりnew 2が生成する.apk
    では、この生成のnew 2をどのように証明しますか.apkと私たちのnew.apkはそっくりですか?
    md 5の値を確認することができます.2つのファイルのmd 5の値が一致すれば、2つのファイルがほぼ同じであることを確認することができます(衝突が同じmd 5の値を生成できるとは言わないでください~~).
    aaa:bsdiff-4.3 zhy$ md5 new.apk 
    MD5 (new.apk) = 0900d0d65f49a0cc3b472e14da11bde7
    aaa:bsdiff-4.3 zhy$ md5 new2.apk 
    MD5 (new2.apk) = 0900d0d65f49a0cc3b472e14da11bde7

    2つのファイルが見えるmd 5はやはり同じですね~~
    ええ、macではないとしたら、どうやってファイルのmd 5を取得しますか?(自分でコードを書いたり、ツールをダウンロードしたりして、このような問題に遭遇しないで、窓を弾いたりして、私は給料を引かれます...)
    では、ここでは、インクリメンタルファイルを生成し、patchを古いファイルと新しいファイルにマージする方法について説明しました.では、プロセス全体を再整理します.
  • サービス側は増分ファイル(本節完了)
  • を作成しました.
  • クライアントはインクリメンタルファイル+アプリケーションのapkをダウンロードし、bspatchを使用して
  • をマージする.
  • で生成された新しいapkは、インストーラ
  • を呼び出す.
    やはりはっきりしていて、それでは主に第2点で、第2点は2つのことがあって、1つはアプリケーションの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;
        }
    }

    (2)bspatch soの作成
    まずクラスを宣言し、nativeメソッドを書きます.次のようにします.
    public class BsPatch {
    
        static {
            System.loadLibrary("bsdiff");
        }
    
        public static native int bspatch(String oldApk, String newApk, String patch);
    
    }

    3つのパラメータはすでに明確になっています.
    同時にmoduleのbuildを忘れないでください.gradleの下:
    defaultConfig {
        ndk {
            moduleName = 'bsdiff'
        }
    }

    この手順では、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;
    }

    方法名には法則があるので、この法則は言うまでもないでしょう~~
    注意しろcにはpatchMethodメソッドはありません.このメソッドは実際にmainメソッドです.直接patchMethodに変更すればいいです.複雑で大丈夫だと思います.文末にソースコードがあります.
    OK、このとき実行してみると、bzlibに依存するようにヒントが表示されますが、実際にはファイルの上部のincludeからも見えます.
    依存している以上、導入しましょう.
    最初にダウンロード:
  • www.bzip.org/downloads.h…
  • www.bzip.org/1.0.6/bzip2…

  • ダウンロードが完了したら、解凍:
    その中のh和.cファイルを抽出し、フォルダcopyをmoduleのapp/main/jniの下に選択します.結果は次のとおりです.
    bsdiffのincludeを変更してください.
    #include "bzip2/bzlib.h"

    再実行;
    次のようなエラーが表示されます.
    Error:(70) multiple definition of `main'

    プロンプトmainメソッドは、エラー情報にmainメソッドが含まれるクラスを繰り返し定義し、これらのクラスのmainメソッドを直接削除することを選択できます.
    削除したら、OK~~
    ここで、私たちはJNIの作成を完了しました.もちろん、ファイルのbsdiffが提供するcソースコードです.
    四、増分更新後のインストール
    上記の操作が完了すると、最後のステップは簡単になります.まず2つのapkを用意します.
    old.apk new.apk

    次にpatchを作成し、次のコードのPATCH.patch;
    これを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());
        }

    読み取りと書き込みの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);
    
        }

    ここで7.0は問題があるかもしれませんが、経路を別のappに暴露したので、FileProviderが実現する必要があるはずです(未実験、推測可能).
    大まかな効果図は以下の通りです.
    五、まとめ
    この機能を単純に使用する場合は、生成したsoファイルを直接コピーし、直接loadLibraryで使用することができます.
    次に、インクリメンタル更新をするとき、patchはあなたの現在のバージョン番号と最新(またはターゲット)バージョンapkに基づいて、diffファイルを下に送り、同時にターゲットapkのmd 5を下に送るべきで、更に合併を終えた後、md 5を検証することを忘れないでください.
    ブログが终わって、とても简単で、主にツールを利用して実现しますが、しかしやはり自分で一回実现することを提案して、一度に走るのはやはりいくつかの时間が必要で、过程の中でもいくつかの穴を発见することができて、自分のJNIに対する熟练度を高めることができます.
    ソース:
  • github.com/hongyangAnd…

  • 直接soを使うこともできます
  • github.com/hongyangAnd…

  • 私の微信の公衆番号に注目してください:hongyangAndroid(注目を歓迎して、すべての干物を見逃さないで、投稿を支持します)
    参照および関連リンク
  • www.daemonology.net/bsdiff/
  • www.bzip.org/downloads.h…
  • blog.csdn.net/hmg25/artic…
  • www.cnblogs.com/lping/p/583…