Android插件机制总结--类的加载
加载插件(另一个.apk或.skin文件)的资源(如:换肤)
实现步骤:所有的资源是加载通过Resource获取的,new Resource() ,核心参数是AssetManager
示例:获取插件apk里的图片资源
try {// 读取本地的一个.skin或 .apk 里面的资源Resources superRes = getResources();// 创建AssetManagerAssetManager asset = AssetManager.class.newInstance();// 添加本地下载好的插件 Method method = AssetManager.class.getDeclaredMethod("addAssetPath",String.class);// method.setAccessible(true); 如果是私有的// 反射执行方法method.invoke(asset, Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator + "app-skin.apk");Resources resource = new Resources(asset,superRes.getDisplayMetrics(),superRes.getConfiguration());// 获取资源 idint drawableId = resource.getIdentifier("img_yangmi","drawable","com.example.skin");Drawable drawable = resource.getDrawable(drawableId);mImageIv.setImageDrawable(drawable);} catch (Exception e) {e.printStackTrace();}
热修复
阿里热修复解决方案:
1.添加依赖库
implementation 'com.alipay.euler:andfix:0.5.0@aar'
2.Application中初始化并加载.apatch包
try {mPatchManager = new PatchManager(this);//获取当前应用的版本PackageManager packageManager = this.getPackageManager();PackageInfo packageInfo = packageManager.getPackageInfo(this.getPackageName(), 0);String versionName = packageInfo.versionName;mPatchManager.init(versionName);//加载apatch包mPatchManager.loadPatch();
} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();
}
3.使用命令生成拆分包:new.apk old.apk fix.apatch
4.app启动后,网络请求后台获取差分包 fix.apatch,下载到本地
File fixFile = new File(Environment.getExternalStorageDirectory(), "fix.apatch");
if (fixFile.exists()) {try {//立即生效不需要重启BaseApplication.mPatchManager.addPatch(fixFile.getAbsolutePath());Log.e(TAG, "修复成功");} catch (IOException e) {e.printStackTrace();Log.e(TAG, "修复失败");}
}
原理:加载dex文件,PATCH.MF文件(解压反编译apatch包后可以看到这个文件)中记录了被修改的类,遍历类,通过反射获取注解信息找到修改的方法,使用jni动态替换方法。
腾讯热修复Tinker的实现方案
类创建的流程:
PathClassLoader
=>BaseDexClassLoader
=>ClassLoader 变量pathList
=>PathDexList 方法findClass
=>dexElements dex或resource元素数组
=>for循环遍历,找到dex便return
实现步骤:
1. 获取已经运行的 dexElement (注意:这里是.dex文件,不是.apk)
1.1 反射获取pathList
1.2 反射获取pathList里面的dexElements
2. 获取下载好的补丁的dexElement
2.1 移动到系统能够访问的dex目录下
2.2 ClassLoader读取fixDex路径
3. 把补丁的dexElement插到 已经运行的dexElement的最前面
3.1 合并
3.2 把合并的数组注入到原来的applicationClassLoader中
插件化架构
1 启动插件apk里的activity,
采用热修复的方案,把插件apk的class加载到ApplicationClassLoader,会出现资源混乱覆盖的问题,可参考360插件的解决方案。
2 使用360开源DroidPlugin插件
GitHub - Qihoo360/DroidPlugin: A plugin framework on android,Run any third-party apk without installation, modification or repackage
增量更新
流程: 服务端生成差分包(update.patch),客户下载差分包进行合并生成新的apk版本,然后安装。
1.服务器生成差分包,供app下载;
2.访问后台接口,需不需要更新版本,如需要则下载;
3.下载完差分包之后,调用方法去合并生成新的apk;
4.校验签名,即获取本地apk的签名,与新的apk做对比;
5.安装新的apk。