【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
Android 插件化系列文章目錄
【Android 插件化】插件化簡介 ( 組件化與插件化 )
【Android 插件化】插件化原理 ( JVM 內存數據 | 類加載流程 )
【Android 插件化】插件化原理 ( 類加載器 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 原理與實現思路 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 類加載器創建 | 資源加載 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 獲取插件入口 Activity 組件 | 加載插件 Resources 資源 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 運行應用 | 代碼整理 )
【Android 插件化】Hook 插件化框架 ( Hook 技術 | 代理模式 | 靜態代理 | 動態代理 )
【Android 插件化】Hook 插件化框架 ( Hook 實現思路 | Hook 按鈕點擊事件 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動過程 | 靜態代理 )
【Android 插件化】Hook 插件化框架 ( 從 Hook 應用角度分析 Activity 啟動流程 一 | Activity 進程相關源碼 )
文章目錄
- Android 插件化系列文章目錄
- 前言
- 一、Activity 任務棧相關源碼
- 1、任務棧管理者 ActivityStackSupervisor
- 2、任務棧 ActivityStack
- 3、Activity 啟動涉及到的組件
- 二、Activity 進程相關源碼
- 1、Instrumentation 源碼分析
- 三、博客資源
前言
上一篇博客 【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動過程 | 靜態代理 ) 使用了靜態代理 , hook 了 Activity 的啟動過程 ;
在 hook Android 的內部流程時 , 注意版本兼容 , 不同的 Android 版本底層源碼實現機制可能有區別 , 需要使用不同的 hook 兼容方式 ;
hook 本身實現起來很簡單 , 但是其 與底層源碼耦合性太高 , 在 Android 8.08.08.0 可以 hook 住的方法 , 在 Android 10.010.010.0 可能就無法使用了 ;
Hook 插件化框架的 難點是版本兼容 , 需要逐個手動兼容 Android 低版本到最新版本 , 一旦系統更新 , 或者某廠商 ROM 更新 , 都要進行兼容測試以及改進 ;
如果 Android 高版本禁止反射 @hide 方法 , 可以在 調用鏈上找到一個非隱藏的方法 , 總能 hook 住 ; 極端情況下 , 使用 動態字節碼技術 , 在運行時修改字節碼數據 , 刪除 @hide 注解 ;
插件化模塊選擇 : 一般的業務邏輯不建議使用插件化 ; 功能比較單一 , 業務邏輯更新比較頻繁 , 并且很重要的模塊 , 使用插件化實現 ;
插件化框架主要是 通過 hook 修改 Instrumentation , 以及 劫持 ActivityManagerService ;
源碼分析的大忌就是死磕每一行源碼的細節 , 只看自己能看懂的 , 每個方法最多看 222 層 , 不要偏離主線 ;
現在的源碼參考資料很多 , 參考別人已經分析完畢的源碼經驗 , 可以節省很多時間 ;
一、Activity 任務棧相關源碼
基于 Android 282828 源碼 , 分析 Activity 的啟動過程 ; ( Android 272727 , 282828 , 292929 中 Android 啟動源碼都進行了不同程度的改進 , 333 個版本的源碼是不同的 )
1、任務棧管理者 ActivityStackSupervisor
Activity 任務棧 : ActivityStack ; Activity 啟動后 , 都加入到 ActivityStack ( 任務棧 ) 中 ;
任務棧管理者 : ActivityStack 由 ActivityStackSupervisor 來管理 , ActivityStackSupervisor 中有兩個數組 , 分別是
- mHomeStack : Launcher 應用使用的任務棧 ;
- mFocusedStack : 當前聚焦的任務棧 , 可以接收輸入 , 或啟動下一個 Activity ;
源碼地址 : frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
2、任務棧 ActivityStack
ActivityStack ( 任務棧 ) 中存在兩個集合 :
- ArrayList<TaskRecord> mTaskHistory : 之前運行的 ( 可能仍在運行 ) 的 Activity 的歷史記錄 , 每個 TaskRecord 都包含了 111 個 ActivityRecord 集合 ;
- ArrayList<ActivityRecord> mLRUActivities : 當前正在運行的 Activity 列表 , 按照最近最少使用算法 LRU 機制進行排序 , 列表中第一個 Activity 是最近最少使用的 ;
ActivityRecord 就是 Activity 的信息 , 注意不是 Activity 的實例對象 , 是歷史任務棧中的一個條目 , 可以代表一個 Activity ;
TaskRecord 中 維護了 111 個 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 啟動涉及到的組件
ActivityThread : 應用主線程 , 每個應用都是從該主線程的 main 函數開始的 ;
- /frameworks/base/core/java/android/app/ActivityThread.java
Instrumentation : 每個 Activity 都持有該類對象 , 檔調用 startActivity 啟動其它 Activity 時 , 就會調用 Instrumentation 進行先關操作 ; ActivityThread 控制 Activity 也是通過該類進行 ; 一個應用中只有一個 Instrumentation 實例對象 ;
- /frameworks/base/core/java/android/app/Instrumentation.java
二、Activity 進程相關源碼
1、Instrumentation 源碼分析
在上一篇博客 【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動過程 | 靜態代理 ) 一、分析 Activity 啟動源碼 章節中分析到 , 在 Activity 中調用 startActivity , 最終調用的是 Instrumentation 的 execStartActivity 方法 ;
在 Instrumentation 中的 newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) 方法 , 用于創建 Activity 實例 , 其中使用了 (Activity)clazz.newInstance() 創建 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;}在另外一個重載的 Activity newActivity(ClassLoader cl, String className, Intent intent) 方法中 , 通過指定 類加載器 ClassLoader , Activity 的全類名 , 也可以創建 Activity 實例對象 ;
Hook 劫持 Activity newActivity(ClassLoader cl, String className, Intent intent) 方法 , 傳入插件包的類加載器 , 和插件包的類名 , 此時就可以初始化帶上下文的 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
總結
以上是生活随笔為你收集整理的【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 插件化】Hook 插件
- 下一篇: 【Android 插件化】Hook 插件