一、增量更新实现思路
1)服务端生成patch差分包:
值得注意的是,需要对用户安装的不同版本,生成不同的差分包,为了是让用户安装的所有版本都升级为最新的版本。
2)客户端请求差分包:
客户端在请求的时候,会把客户端的版本发送给服务端,服务端根据不同的版本,下发不同的差分包。
3)客户端合成新的安装包:
客户端通过调用native代码,将安装的包和下载的差分包生成新的安装包。这时需要对生成的安装包进行md5校验,保证生成的和最新的包是一样的。
二、服务端生成差分包的方法
通过bsdiff命令生成差分包:bsdiff old.apk new.apk patch.apk
old.apk是原apk路径,new.apk目前apk路径,patch.apk是生成的差分包的文件路径。
命令下载:https://download.csdn.net/download/niuyongzhi/88366198?spm=1001.2014.3001.5501
执行命令:
生成的文件:
三、客户端生成新的安装包
在没有服务端的情况下,客户端测试,可以把生成的patch包拷贝到sdcard目录下,当做是已经从服务端下载好的patch包。
客户端调用native代码:
public static native int patch(String oldApkPath, String newApkPath,
String patchPath);
1)oldApkpath:安装在手机中的Apk包,可以通过一下代码获取。
2)newApkPath:即合成新的Apk后的路径。
3)patchPath:存放在sdcard的差分包路径。
/**
* 获取已安装Apk路径
* packageName 报名
*/
public static String getOldApkPath(Context context, String packageName) {
if (TextUtils.isEmpty(packageName))
return null;
try {
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(packageName, 0);
return appInfo.sourceDir;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
4)新的安装包生成后,进行md5校验,校验通过调用安装apk代码:
MD5校验代码:
private static String bytes2Hex(byte[] src) {
char[] res = new char[src.length * 2];
final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
for (int i = 0, j = 0; i < src.length; i++) {
res[j++] = hexDigits[src[i] >>> 4 & 0x0f];
res[j++] = hexDigits[src[i] & 0x0f];
}
return new String(res);
}
public static String getMd5ByFile(File file) {
String value = null;
FileInputStream in = null;
try {
in = new FileInputStream(file);
MessageDigest digester = MessageDigest.getInstance("MD5");
byte[] bytes = new byte[8192];
int byteCount;
while ((byteCount = in.read(bytes)) > 0) {
digester.update(bytes, 0, byteCount);
}
value = bytes2Hex(digester.digest());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return value;
}
调用系统api进行新包安装:
public static void installApk(Context context, File file) {
boolean canInstall = true;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
canInstall = context.getPackageManager().canRequestPackageInstalls();
}
if (canInstall) {
Uri uri;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 24) {
uri = FileProvider.getUriForFile(context, "com.example.patch.fileProvider", file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
uri = Uri.fromFile(file);
}
intent.setDataAndType(uri, "application/vnd.android.package-archive");
context.startActivity(intent);
} else {
context.startActivity(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES));
}
}
这时,就完成了apk增量升级。
四:客户端编译native代码生成so库,供java端进行新版合成的进行调用。
1)File->New->New Module 选择Android Native Library,创建一个Native module项目。
2)将bip2的c层代码拷贝到cpp目录下。
3)配置CMakeLists.txt。红色框的代码是要编译的代码。绿色框中apk_pacth是编译后生成的so库的名字。
4)java层创建Native方法
public class PatchUtils {
public static native int patch(String oldApkPath, String newApkPath,
String patchPath);
}
5)Android Studio会自动提示生成JNI层对应的方法。如果没有,通过javac -h 或javah生成头文件。
javac -h 生成的头文件命令由三部分组成:
1)javac -h
2) 生成的头文件存放的路径
3)要生成头文件Native方法所在java类的路径。
命令执行会生成一个class文件和一个.h文件。
头文件长这个样子:
4)编写JNI层代码。JNI大家都不陌生,他是连接Java和Native的桥梁。
/*
* Class: com_example_utils_PatchUtils
* Method: patch
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_example_utils_PatchUtils_patch(JNIEnv *env,
jclass obj, jstring old, jstring new, jstring patch) {
char * ch[4];
ch[0] = "bspatch";
ch[1] = (char*) ((*env)->GetStringUTFChars(env, old, 0));
ch[2] = (char*) ((*env)->GetStringUTFChars(env, new, 0));
ch[3] = (char*) ((*env)->GetStringUTFChars(env, patch, 0));
__android_log_print(ANDROID_LOG_INFO, "ApkPatchLibrary", "old = %s ", ch[1]);
__android_log_print(ANDROID_LOG_INFO, "ApkPatchLibrary", "new = %s ", ch[2]);
__android_log_print(ANDROID_LOG_INFO, "ApkPatchLibrary", "patch = %s ", ch[3]);
int ret = applypatch(4, ch);
__android_log_print(ANDROID_LOG_INFO, "ApkPatchLibrary", "applypatch result = %d ", ret);
(*env)->ReleaseStringUTFChars(env, old, ch[1]);
(*env)->ReleaseStringUTFChars(env, new, ch[2]);
(*env)->ReleaseStringUTFChars(env, patch, ch[3]);
return ret;
}
bsdiff命令下载地址:https://download.csdn.net/download/niuyongzhi/88366198
代码下载地址:https://download.csdn.net/download/niuyongzhi/88374161