android增量更新中差分包生成以及合成

網上看了很多的帖子,單對於第一次接觸增量更新的朋友,會碰到各種坑,浪費大量時間。

說到增量更新並非熱修復,增量更新具體實現邏輯是:根據新舊包之間的差異生成對應的二進制差異包文件,然後將此差異文件合成到老的 apk中使之含有新版本的包的代碼來達到更新效果。

下面是關於個人查閱資料總結的分差包生成以及合成的具體步驟

一、首先需要下載對應的差分包生成合成的jni文件(bsdiff-4.3、bzip2-1.0.6)

bsdiff-4.3具體文件內容如圖:

bzip2-1.0.6具體文件內容如圖:

二、要確定的你android studio版本是否是大於2.2,大於2.2的版本可以直接創建帶native項目的工程,創建項目時直接勾選 include c++ support選項如圖

三、當然AS還要支持ndk編譯,如果不支持就需要去安裝一些插件主要勾選 CMAKE 、LLDB、NDK安裝,如圖:

四、前提工作已經準備就緒,按流程創建一個勾選了include C++ support項目。

五、將前面下載下來的工具包裏面的c(庫文件)導入到cpp文件中,具體需要導入的文件可以參考下圖其中bs.h和bs.c是我們需要自己編寫的文件:


bs.h頭文件:

//
// Created by Administrator on 2017/8/15 0015.
//

#ifndef BSDIFFPATCH_BS_H
#define BSDIFFPATCH_BS_H
#endif //BSDIFFPATCH_BS_H
#include <malloc.h>
#include <jni.h>
JNIEXPORT jint JNICALL
Java_com_example_administrator_applicationc_MainActivity_patch
        (JNIEnv *env, jobject instance, jstring oldpath_, jstring newpath_,jstring patch_);

JNIEXPORT jint JNICALL
Java_com_example_administrator_applicationc_MainActivity_diff
        (JNIEnv *env, jobject instance, jstring oldpath_, jstring newpath_, jstring patch_);
bs.c類文件:

//
// Created by Administrator on 2017/8/15 0015.
//


#include "bs.h"
#include "bsdiff.c"
#include "bspatch.c"


JNIEXPORT jint JNICALL
               Java_com_example_administrator_applicationc_MainActivity_patch
                       (JNIEnv *env, jobject instance, jstring oldpath_, jstring newpath_,jstring patch_) {
    const char* argv[4];
    argv[0] = "bspatch";
    argv[1] = (*env)->GetStringUTFChars(env,oldpath_, 0);
    argv[2] = (*env)->GetStringUTFChars(env,newpath_, 0);
    argv[3] = (*env)->GetStringUTFChars(env, patch_, 0);
    //該函數用於合併差分包
    mergeDiffApk(4,argv);
    (*env)->ReleaseStringUTFChars(env,oldpath_, argv[1]);
    (*env)->ReleaseStringUTFChars(env,newpath_, argv[2]);
    (*env)->ReleaseStringUTFChars(env,patch_,argv[3]);
    free(argv);
    return 0;
}

JNIEXPORT jint JNICALL
               Java_com_example_administrator_applicationc_MainActivity_diff
                       (JNIEnv *env, jobject instance, jstring oldpath_, jstring newpath_, jstring patch_) {
    int argc = 4;
    char *argv[argc];
    argv[0] = (char *) "bspatch";
    argv[1] = (char *) (*env)->GetStringUTFChars(env, oldpath_, 0);
    argv[2] = (char *) (*env)->GetStringUTFChars(env, newpath_, 0);
    argv[3] = (char *) (*env)->GetStringUTFChars(env, patch_, 0);
    jint result = generateDiffApk(argc, argv);
    (*env)->ReleaseStringUTFChars(env, oldpath_, argv[1]);
    (*env)->ReleaseStringUTFChars(env, newpath_, argv[2]);
    (*env)->ReleaseStringUTFChars(env, patch_, argv[3]);
    return result;

}

其中的mergeDiffApk方法在bspatch.c中,將bspatch.c中的main方法名稱改成mergeDiffApk.
其中的generateDiffApk方法在bsdiff.c中,將bsdiff.c中的mian方法名改成generateDiffApk.
六、jni部分已經準備就緒,下面是關於MainActivity的調用以及新、舊包的準備。
先準備好需要生成差異包的apk,將此新舊apk放到sdk卡的根目錄中如圖:

放好包後就是執行ManiActivity中的差分包執行方法,MainActivity具體測試代碼如下:
package com.example.administrator.applicationc;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    //舊版本
    String old =getsdpath()+"hello.apk";
    //新版本
    String newp = getsdpath()+"hehehe.apk";
    //差分包
    String patch = getsdpath()+"patch.patch";
    //舊版apk和差分包合併生成的新版apk
    String tmp = getsdpath()+"new.apk";


    private final  String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        findViewById(R.id.bt_diff).setOnClickListener(this);
        findViewById(R.id.bt_patch).setOnClickListener(this);


        findViewById(R.id.bt_one).setOnClickListener(this);
        findViewById(R.id.bt_two).setOnClickListener(this);

        int REQUEST_EXTERNAL_STORAGE = 1;
        String[] PERMISSIONS_STORAGE = {
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        int permission = ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(
                    MainActivity.this,
                    PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE
            );
        }

    }



    private String getsdpath(){
        return Environment.getExternalStorageDirectory().getPath()+ File.separator;
    }
    //生成差分包
    public native int diff(String oldpath,String newpath,String patch);
    //舊apk和差分包合併
    public native int patch(String oldpath,String newpath,String patch);
    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.bt_diff:
                Log.e(TAG,"patch_old:"+old);
                Log.e(TAG,"patch_newp:"+newp);
                Log.e(TAG,"patch_patch:"+patch);
                long s = System.currentTimeMillis();

                diff(old,newp,patch);
                long s1 = System.currentTimeMillis();
                Toast.makeText(MainActivity.this,"生成差分包成功,用時:"+(s1-s)+"ms",Toast.LENGTH_SHORT).show();
                break;
            case R.id.bt_patch:
                long s2 = System.currentTimeMillis();
                Log.e(TAG,"patch_old:"+old);
                Log.e(TAG,"patch_newp:"+tmp);
                Log.e(TAG,"patch_patch:"+patch);
                        patch(old,tmp,patch);
                long s3 = System.currentTimeMillis();
                Toast.makeText(this,"差分包合併成功,用時:"+(s3-s2)+"ms",Toast.LENGTH_SHORT).show();
                break;
            case R.id.bt_one:
                Toast.makeText(this,"按鈕1",Toast.LENGTH_SHORT).show();
                break;
            case R.id.bt_two:
                Toast.makeText(this,"按鈕2",Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

當然必要的權限不能少
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章