網上看了很多的帖子,單對於第一次接觸增量更新的朋友,會碰到各種坑,浪費大量時間。
說到增量更新並非熱修復,增量更新具體實現邏輯是:根據新舊包之間的差異生成對應的二進制差異包文件,然後將此差異文件合成到老的 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>