常见第三方插件化工具
droidplugin 已弃用;
replugin 360的 在更新 耦合度低推荐;
VirtualAPK 滴滴的在更新 耦合度高推荐;
dynamicAPK携程 搜了下百度没最新的
反射影响性能原因
1 产生大量的零时对象
2 检查可见性
3 如果经常调用某块,会生成字节码 (没有优化的,不超过1000次就还好)
4 类型转换 封箱拆箱
ClassLoader
bootclassloader 加载系统的类 sdk 的
pathclassloader 加载自己写的类 应用的
dexclassloader 8.0之后 和pathclassloader没什么区别;这个是谷歌考虑兼容 留给用户用的,而pathclassloader是谷歌自己用的
dexclassloader—ClassLoader.loadclass双亲委派机制 到 pathClassLoader的 loadclass 然后再到 bootclassLoader 的loadclass (这个就在ClassLoader 里面)
双亲委派机制
避免重复加载
安全性,防止核心api串改
Basedexclassloader 中的dexpath 中的dexelement数组
findclass 主要关注自己的, 其中element[] dexElements 是所有类加载 element是具体 的类;
我们要做的是在dexElements上面加上插件的类 一起打包
类加载
- 获取宿主dexElements
- 获取插件dexElements
- 合并两个dexElements
- 将新的dexElements 赋值到 宿主dexElements
- dexElements -- DexPathList类的对象 -- BaseDexClassLoader的对象,类加载器
- 获取的是宿主的类加载器 --- 反射 dexElements 宿主
- 获取的是插件的类加载器 --- 反射 dexElements 插件
Class<?> clazz= Class.forName("dalvik.system.BaseDexClassLoader");
Field pathListField = clazz.getDeclaredField("pathList");
pathListField.setAccessible(true);
Class<?> dexPathListClass = Class.forName("dalvik.system.DexPathList");
Field dexElementsField = dexPathListClass.getDeclaredField("dexElements");
dexElementsField.setAccessible(true);
ClassLoader pathClassLoader = context.getClassLoader();
Object hostPathList = pathListField.get(pathClassLoader);
Object[] hostDexElements = (Object[]) dexElementsField.get(hostPathList);
ClassLoader pluginClassLoader = new DexClassLoader("",context.getCacheDir().getAbsolutePath(),"",pathClassLoader);
Object pluginPathList = pathListField.get(pluginClassLoader);
Object[] pluginDexElements = (Object[]) dexElementsField.get(pluginPathList);
Object[] newDexElements = (Object[]) Array.newInstance(hostDexElements.getClass().getComponentType(),
hostDexElements.length+pluginDexElements.length);
System.arraycopy(hostDexElements,0,newDexElements,0,hostDexElements.length);
System.arraycopy(pluginDexElements,0,newDexElements,hostDexElements.length,pluginDexElements.length);
dexElementsField.set(hostDexElements,newDexElements);
activity启动 29版本android10
- Activity
startActivityForResult 调用 Instrumentation.execStartActivity - Instrumentation
这里进行hook 进行ams替换
public static IActivityTaskManager getService(){
return IActivityTaskManagerSingleton.get();
}
- ActivityTaskManagerService
用途管理Activity及其容器,8以后加入的分摊AMS职责的
startActivity 到 startActivityAsUser ...excute() - ActivityStarter
ActivityStarter.excute开启Activity旅程;处理请求参数和一些检查;
startActivityUnchecked...resumeFocusedStacksTopActivies - RootActivityContainer(自己跟的是这个,网上有文章说RootWindowContainer?空了看看)
resumeTopActivityUncheckedLocked - ActivityStack TaskRecord有关的处理
resumeTopActivityInnerLocked中
mStackSupervisor.startSpecificActivity - ActivityStackSupervisor
对ActivityStack的管理 - ActivityStackSupervisor
realStartActivityLocked中
clientTransaction.addCallback(LaunchActivityItem
mService.getLifecycleManager().scheduleTransaction(clientTransaction); --ClientLifecycleManager 通过binder联系启动客户端activity - ClientLifecycleManager
scheduleTransaction IApplicationThread 到客户端
- ApplicationThread -------回到客户端
ApplicationThread的scheduleTransaction调用ActivityThread的此方法,实际是ClientTransactionHandler里面的 - ClientTransactionHandler
sendmessage EXCUTE_TRANSACTION 发送消息 - ActivityThread 中的H mH
到handler里面处理 handleMessage EXCUTE_TRANSACTION
msg.obj 这里进行hook 处理返回的intent的替换
Hook AMS 29版本
- 反射获取IActivityTaskManager 和Singleton
- 动态代理 把startActivity 前把intent替换为插件代理Activity(intent中要加入插件activity的intent)
- 注意:动态代理要替换IActivityTaskManager 才有效果
Hook Handler 29版本
- 来个Handler.callback 里面处理159 EXCUTE_TRANSACTION
- 通过ClientTransaction 循环 mActivityCallbacks 拿到 LaunchActivityItem(ClientTransactionItem的实现)实例 得到mIntent 换回来
4.callback要替换系统的 反射来替换
Resource
- 通过AssetManager做资源处理
- 反射 调用 addAssetPath 新加一份插件的资源
- new Resources 来得到插件的Resource
- 插件要自己新建context (通过ContextThemeWrapper)
- 在新建的context里面 通过反射把mResources换掉,换成插件的
- 完工