常見第三方插件化工具
droidplugin 已棄用;
replugin 360的 在更新 耦合度低推薦;
VirtualAPK 滴滴的在更新 耦合度高推薦;
dynamicAPK攜程 搜了下百度沒最新的
反射影響性能原因
1 產生大量的零時對象
2 檢查可見性
3 如果經(jīng)常調用某塊,會生成字節(jié)碼 (沒有優(yōu)化的,不超過1000次就還好)
4 類型轉換 封箱拆箱
ClassLoader
bootclassloader 加載系統(tǒng)的類 sdk 的
pathclassloader 加載自己寫的類 應用的
dexclassloader 8.0之后 和pathclassloader沒什么區(qū)別;這個是谷歌考慮兼容 留給用戶用的,而pathclassloader是谷歌自己用的
dexclassloader—ClassLoader.loadclass雙親委派機制 到 pathClassLoader的 loadclass 然后再到 bootclassLoader 的loadclass (這個就在ClassLoader 里面)
雙親委派機制
避免重復加載
安全性,防止核心api串改
Basedexclassloader 中的dexpath 中的dexelement數(shù)組
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旅程;處理請求參數(shù)和一些檢查;
startActivityUnchecked...resumeFocusedStacksTopActivies - RootActivityContainer(自己跟的是這個,網(wǎng)上有文章說RootWindowContainer?空了看看)
resumeTopActivityUncheckedLocked - ActivityStack TaskRecord有關的處理
resumeTopActivityInnerLocked中
mStackSupervisor.startSpecificActivity - ActivityStackSupervisor
對ActivityStack的管理 - ActivityStackSupervisor
realStartActivityLocked中
clientTransaction.addCallback(LaunchActivityItem
mService.getLifecycleManager().scheduleTransaction(clientTransaction); --ClientLifecycleManager 通過binder聯(lián)系啟動客戶端activity - ClientLifecycleManager
scheduleTransaction IApplicationThread 到客戶端
- ApplicationThread -------回到客戶端
ApplicationThread的scheduleTransaction調用ActivityThread的此方法,實際是ClientTransactionHandler里面的 - ClientTransactionHandler
sendmessage EXCUTE_TRANSACTION 發(fā)送消息 - ActivityThread 中的H mH
到handler里面處理 handleMessage EXCUTE_TRANSACTION
msg.obj 這里進行hook 處理返回的intent的替換
Hook AMS 29版本
- 反射獲取IActivityTaskManager 和Singleton
- 動態(tài)代理 把startActivity 前把intent替換為插件代理Activity(intent中要加入插件activity的intent)
- 注意:動態(tài)代理要替換IActivityTaskManager 才有效果
Hook Handler 29版本
- 來個Handler.callback 里面處理159 EXCUTE_TRANSACTION
- 通過ClientTransaction 循環(huán) mActivityCallbacks 拿到 LaunchActivityItem(ClientTransactionItem的實現(xiàn))實例 得到mIntent 換回來
4.callback要替換系統(tǒng)的 反射來替換
Resource
- 通過AssetManager做資源處理
- 反射 調用 addAssetPath 新加一份插件的資源
- new Resources 來得到插件的Resource
- 插件要自己新建context (通過ContextThemeWrapper)
- 在新建的context里面 通過反射把mResources換掉,換成插件的
- 完工