Android 增量更新和升级
- - CSDN博客推荐文章在年初的时候,尝试了一把热修复技术,当时选择的是阿里的andfix,使用起来也很简单,这里就不在多少,如果你对andfix有兴趣请链接: 点击打开链接. 虽然网上将热修复的文章很多,不过我还是想说原理,然后配合代码,我想这样大家理解更加深刻. 其实就是用ClassLoader加载机制,覆盖掉有问题的方法.
在年初的时候,尝试了一把热修复技术,当时选择的是阿里的andfix,使用起来也很简单,这里就不在多少,如果你对andfix有兴趣请链接: 点击打开链接。虽然网上将热修复的文章很多,不过我还是想说原理,然后配合代码,我想这样大家理解更加深刻。
拷贝资源 | 拷贝旧版本APK以及新版本APK到SD卡。为了后面进行生成差分包 |
安装旧版本APK | 安装旧版本的APK |
生成补丁 | 生成差分包。这个实际上应该是在服务端完成 |
打补丁 | 通过差分包及旧版本APK生成新版本APK |
安装新版本APK | 安装生成的新版本APK |
获取某个应用的APK安装文件 | 在真正的增量更新过程中,旧版本Apk应该从/data/app底下获取,拷贝到SD卡,进行打补丁。当然,也可以不拷贝,直接使用该路径。 |
String srcDir = Environment.getExternalStorageDirectory().toString() + "/DaemonProcess-1.apk"; String destDir1 = Environment.getExternalStorageDirectory().toString() + "/DaemonProcess-2.apk"; String destDir2 = Environment.getExternalStorageDirectory().toString() + "/DaemonProcess-3.apk"; String patchDir = Environment.getExternalStorageDirectory().toString() + "/DaemonProcess.patch";
String appDir = getPackageManager().getApplicationInfo(packageName, 0).sourceDir;destDir1:新版本的apk路径。
public class DiffUtils { static DiffUtils instance; public static DiffUtils getInstance() { if (instance == null) instance = new DiffUtils(); return instance; } static { System.loadLibrary("ApkPatchLibrary"); } /** * native方法 比较路径为oldPath的apk与newPath的apk之间差异,并生成patch包,存储于patchPath */ public native int genDiff(String oldApkPath, String newApkPath, String patchPath); }合成新包Native方法:
public class PatchUtils { static PatchUtils instance; public static PatchUtils getInstance() { if (instance == null) instance = new PatchUtils(); return instance; } static { System.loadLibrary("ApkPatchLibrary"); } /** * native方法 使用路径为oldApkPath的apk与路径为patchPath的补丁包,合成新的apk,并存储于newApkPath */ public native int patch(String oldApkPath, String newApkPath, String patchPath); }
private class CopyTask extends AsyncTask<String, Void, Integer> { @Override protected Integer doInBackground(String... params) { for (int i = 0; i < params.length; i += 2) { try { File file = new File(params[i]); if (!file.exists()) FileUtils.createFile(file); InputStream is; OutputStream os = new FileOutputStream(params[i]); is = getAssets().open(params[i + 1]); byte[] buffer = new byte[1024]; int length = is.read(buffer); while (length > 0) { os.write(buffer, 0, length); length = is.read(buffer); } os.flush(); is.close(); os.close(); } catch (Exception e) { handler.obtainMessage(1).sendToTarget(); return null; } } handler.obtainMessage(0).sendToTarget(); return null; } @Override protected void onPostExecute(Integer integer) { super.onPostExecute(integer); loadding.setVisibility(View.GONE); } }2,合成新的apk(jni会自动判断是否合成成功)
private class PatchTask extends AsyncTask<String, Void, Integer> { @Override protected Integer doInBackground(String... params) { try { int result = PatchUtils.getInstance().patch(srcDir, destDir2, patchDir); if (result == 0) { handler.obtainMessage(4).sendToTarget(); return WHAT_SUCCESS; } else { handler.obtainMessage(5).sendToTarget(); return WHAT_FAIL_PATCH; } } catch (Exception e) { e.printStackTrace(); } return WHAT_FAIL_PATCH; } @Override protected void onPostExecute(Integer integer) { super.onPostExecute(integer); loadding.setVisibility(View.GONE); } }3,安装新的apk
private void install(String dir) { String command = "chmod 777 " + dir; Runtime runtime = Runtime.getRuntime(); try { runtime.exec(command); // 可执行权限 } catch (IOException e) { e.printStackTrace(); } Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(Uri.parse("file://" + dir), "application/vnd.android.package-archive"); startActivity(intent); }
服务端工具以及源码位于Server目录下。目前只在Linux64位的系统下编译,其他系统大家可自行编译。Linux下的可直接修改makefile,windows下可用VC编译。
<!--命令 oldApk newApk patch--> ./linux-x86_64/Diff DaemonProcess-1.apk DaemonProcess-2.apk dp.patch
<!--命令 oldApk newApk patch--> ./linux-x86_64/Patch DaemonProcess-1.apk DaemonProcess-3.apk dp.patch