加载插件(另一个.apk或.skin文件)的资源(如:换肤)
实现步骤:所有的资源是加载通过Resource获取的,new Resource() ,核心参数是AssetManager
示例:获取插件apk里的图片资源
try {
// 读取本地的一个.skin或 .apk 里面的资源
Resources superRes = getResources();
// 创建AssetManager
AssetManager 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());
// 获取资源 id
int 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插件
增量更新
流程: 服务端生成差分包(update.patch),客户下载差分包进行合并生成新的apk版本,然后安装。
1.服务器生成差分包,供app下载;
2.访问后台接口,需不需要更新版本,如需要则下载;
3.下载完差分包之后,调用方法去合并生成新的apk;
4.校验签名,即获取本地apk的签名,与新的apk做对比;
5.安装新的apk。