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