【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
Android 插件化系列文章目錄
【Android 插件化】插件化簡(jiǎn)介 ( 組件化與插件化 )
【Android 插件化】插件化原理 ( JVM 內(nèi)存數(shù)據(jù) | 類加載流程 )
【Android 插件化】插件化原理 ( 類加載器 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 原理與實(shí)現(xiàn)思路 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 類加載器創(chuàng)建 | 資源加載 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 獲取插件入口 Activity 組件 | 加載插件 Resources 資源 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 運(yùn)行應(yīng)用 | 代碼整理 )
【Android 插件化】Hook 插件化框架 ( Hook 技術(shù) | 代理模式 | 靜態(tài)代理 | 動(dòng)態(tài)代理 )
【Android 插件化】Hook 插件化框架 ( Hook 實(shí)現(xiàn)思路 | Hook 按鈕點(diǎn)擊事件 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動(dòng)過(guò)程 | 靜態(tài)代理 )
【Android 插件化】Hook 插件化框架 ( 從 Hook 應(yīng)用角度分析 Activity 啟動(dòng)流程 一 | Activity 進(jìn)程相關(guān)源碼 )
文章目錄
- Android 插件化系列文章目錄
- 前言
- 一、Activity 任務(wù)棧相關(guān)源碼
- 1、任務(wù)棧管理者 ActivityStackSupervisor
- 2、任務(wù)棧 ActivityStack
- 3、Activity 啟動(dòng)涉及到的組件
- 二、Activity 進(jìn)程相關(guān)源碼
- 1、Instrumentation 源碼分析
- 三、博客資源
前言
上一篇博客 【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動(dòng)過(guò)程 | 靜態(tài)代理 ) 使用了靜態(tài)代理 , hook 了 Activity 的啟動(dòng)過(guò)程 ;
在 hook Android 的內(nèi)部流程時(shí) , 注意版本兼容 , 不同的 Android 版本底層源碼實(shí)現(xiàn)機(jī)制可能有區(qū)別 , 需要使用不同的 hook 兼容方式 ;
hook 本身實(shí)現(xiàn)起來(lái)很簡(jiǎn)單 , 但是其 與底層源碼耦合性太高 , 在 Android 8.08.08.0 可以 hook 住的方法 , 在 Android 10.010.010.0 可能就無(wú)法使用了 ;
Hook 插件化框架的 難點(diǎn)是版本兼容 , 需要逐個(gè)手動(dòng)兼容 Android 低版本到最新版本 , 一旦系統(tǒng)更新 , 或者某廠商 ROM 更新 , 都要進(jìn)行兼容測(cè)試以及改進(jìn) ;
如果 Android 高版本禁止反射 @hide 方法 , 可以在 調(diào)用鏈上找到一個(gè)非隱藏的方法 , 總能 hook 住 ; 極端情況下 , 使用 動(dòng)態(tài)字節(jié)碼技術(shù) , 在運(yùn)行時(shí)修改字節(jié)碼數(shù)據(jù) , 刪除 @hide 注解 ;
插件化模塊選擇 : 一般的業(yè)務(wù)邏輯不建議使用插件化 ; 功能比較單一 , 業(yè)務(wù)邏輯更新比較頻繁 , 并且很重要的模塊 , 使用插件化實(shí)現(xiàn) ;
插件化框架主要是 通過(guò) hook 修改 Instrumentation , 以及 劫持 ActivityManagerService ;
源碼分析的大忌就是死磕每一行源碼的細(xì)節(jié) , 只看自己能看懂的 , 每個(gè)方法最多看 222 層 , 不要偏離主線 ;
現(xiàn)在的源碼參考資料很多 , 參考別人已經(jīng)分析完畢的源碼經(jīng)驗(yàn) , 可以節(jié)省很多時(shí)間 ;
一、Activity 任務(wù)棧相關(guān)源碼
基于 Android 282828 源碼 , 分析 Activity 的啟動(dòng)過(guò)程 ; ( Android 272727 , 282828 , 292929 中 Android 啟動(dòng)源碼都進(jìn)行了不同程度的改進(jìn) , 333 個(gè)版本的源碼是不同的 )
1、任務(wù)棧管理者 ActivityStackSupervisor
Activity 任務(wù)棧 : ActivityStack ; Activity 啟動(dòng)后 , 都加入到 ActivityStack ( 任務(wù)棧 ) 中 ;
任務(wù)棧管理者 : ActivityStack 由 ActivityStackSupervisor 來(lái)管理 , ActivityStackSupervisor 中有兩個(gè)數(shù)組 , 分別是
- mHomeStack : Launcher 應(yīng)用使用的任務(wù)棧 ;
- mFocusedStack : 當(dāng)前聚焦的任務(wù)棧 , 可以接收輸入 , 或啟動(dòng)下一個(gè) Activity ;
源碼地址 : frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
2、任務(wù)棧 ActivityStack
ActivityStack ( 任務(wù)棧 ) 中存在兩個(gè)集合 :
- ArrayList<TaskRecord> mTaskHistory : 之前運(yùn)行的 ( 可能仍在運(yùn)行 ) 的 Activity 的歷史記錄 , 每個(gè) TaskRecord 都包含了 111 個(gè) ActivityRecord 集合 ;
- ArrayList<ActivityRecord> mLRUActivities : 當(dāng)前正在運(yùn)行的 Activity 列表 , 按照最近最少使用算法 LRU 機(jī)制進(jìn)行排序 , 列表中第一個(gè) Activity 是最近最少使用的 ;
ActivityRecord 就是 Activity 的信息 , 注意不是 Activity 的實(shí)例對(duì)象 , 是歷史任務(wù)棧中的一個(gè)條目 , 可以代表一個(gè) Activity ;
TaskRecord 中 維護(hù)了 111 個(gè) ArrayList<ActivityRecord> , 用于保存 ActivityRecord ;
class ActivityStack<T extends StackWindowController> extends ConfigurationContainerimplements StackWindowListener {/*** The back history of all previous (and possibly still* running) activities. It contains #TaskRecord objects.*/private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();/*** List of running activities, sorted by recent usage.* The first entry in the list is the least recently used.* It contains HistoryRecord objects.*/final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<>(); }3、Activity 啟動(dòng)涉及到的組件
ActivityThread : 應(yīng)用主線程 , 每個(gè)應(yīng)用都是從該主線程的 main 函數(shù)開(kāi)始的 ;
- /frameworks/base/core/java/android/app/ActivityThread.java
Instrumentation : 每個(gè) Activity 都持有該類對(duì)象 , 檔調(diào)用 startActivity 啟動(dòng)其它 Activity 時(shí) , 就會(huì)調(diào)用 Instrumentation 進(jìn)行先關(guān)操作 ; ActivityThread 控制 Activity 也是通過(guò)該類進(jìn)行 ; 一個(gè)應(yīng)用中只有一個(gè) Instrumentation 實(shí)例對(duì)象 ;
- /frameworks/base/core/java/android/app/Instrumentation.java
二、Activity 進(jìn)程相關(guān)源碼
1、Instrumentation 源碼分析
在上一篇博客 【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動(dòng)過(guò)程 | 靜態(tài)代理 ) 一、分析 Activity 啟動(dòng)源碼 章節(jié)中分析到 , 在 Activity 中調(diào)用 startActivity , 最終調(diào)用的是 Instrumentation 的 execStartActivity 方法 ;
在 Instrumentation 中的 newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) 方法 , 用于創(chuàng)建 Activity 實(shí)例 , 其中使用了 (Activity)clazz.newInstance() 創(chuàng)建 Activity 示例 ,
public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id,Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException {Activity activity = (Activity)clazz.newInstance();ActivityThread aThread = null;// Activity.attach expects a non-null Application Object.if (application == null) {application = new Application();}activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,info, title, parent, id,(Activity.NonConfigurationInstances)lastNonConfigurationInstance,new Configuration(), null /* referrer */, null /* voiceInteractor */,null /* window */, null /* activityConfigCallback */);return activity;}在另外一個(gè)重載的 Activity newActivity(ClassLoader cl, String className, Intent intent) 方法中 , 通過(guò)指定 類加載器 ClassLoader , Activity 的全類名 , 也可以創(chuàng)建 Activity 實(shí)例對(duì)象 ;
Hook 劫持 Activity newActivity(ClassLoader cl, String className, Intent intent) 方法 , 傳入插件包的類加載器 , 和插件包的類名 , 此時(shí)就可以初始化帶上下文的 Activity ,
public Activity newActivity(ClassLoader cl, String className,Intent intent)throws InstantiationException, IllegalAccessException,ClassNotFoundException {String pkg = intent != null && intent.getComponent() != null? intent.getComponent().getPackageName() : null;return getFactory(pkg).instantiateActivity(cl, className, intent);}public class Instrumentation {public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id,Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException {Activity activity = (Activity)clazz.newInstance();ActivityThread aThread = null;// Activity.attach expects a non-null Application Object.if (application == null) {application = new Application();}activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,info, title, parent, id,(Activity.NonConfigurationInstances)lastNonConfigurationInstance,new Configuration(), null /* referrer */, null /* voiceInteractor */,null /* window */, null /* activityConfigCallback */);return activity;}public Activity newActivity(ClassLoader cl, String className,Intent intent)throws InstantiationException, IllegalAccessException,ClassNotFoundException {String pkg = intent != null && intent.getComponent() != null? intent.getComponent().getPackageName() : null;return getFactory(pkg).instantiateActivity(cl, className, intent);}public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {IApplicationThread whoThread = (IApplicationThread) contextThread;Uri referrer = target != null ? target.onProvideReferrer() : null;if (referrer != null) {intent.putExtra(Intent.EXTRA_REFERRER, referrer);}if (mActivityMonitors != null) {synchronized (mSync) {final int N = mActivityMonitors.size();for (int i=0; i<N; i++) {final ActivityMonitor am = mActivityMonitors.get(i);ActivityResult result = null;if (am.ignoreMatchingSpecificIntents()) {result = am.onStartActivity(intent);}if (result != null) {am.mHits++;return result;} else if (am.match(who, null, intent)) {am.mHits++;if (am.isBlocking()) {return requestCode >= 0 ? am.getResult() : null;}break;}}}}try {intent.migrateExtraStreamToClipData();intent.prepareToLeaveProcess(who);int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target != null ? target.mEmbeddedID : null,requestCode, 0, null, options);checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;} }
三、博客資源
博客資源 :
- GitHub : https://github.com/han1202012/Plugin_Hook
總結(jié)
以上是生活随笔為你收集整理的【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Android 插件化】Hook 插件
- 下一篇: 【Android 插件化】Hook 插件