网上看了很多的帖子,单对于第一次接触增量更新的朋友,会碰到各种坑,浪费大量时间。
说到增量更新并非热修复,增量更新具体实现逻辑是:根据新旧包之间的差异生成对应的二进制差异包文件,然后将此差异文件合成到老的 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 #includebs.c类文件:#include 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_);
// // 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>