摘要:什么樣的對(duì)象容易找到靜態(tài)變量和單例。在一個(gè)進(jìn)程之內(nèi),靜態(tài)變量和單例變量是相對(duì)不容易發(fā)生變化的,因此非常容易定位,而普通的對(duì)象則要么無(wú)法標(biāo)志,要么容易改變。
前言
為了實(shí)現(xiàn) App 的快速迭代更新,基于 H5 Hybrid 的解決方案有很多,由于 webview 本身的性能問(wèn)題,也隨之出現(xiàn)了很多基于 JS 引擎實(shí)現(xiàn)的原生渲染的方案,例如 React Native、weex 等,而國(guó)內(nèi)一線大廠基本上主要還是 Android 插件化解決大部分的更新問(wèn)題,對(duì)于部分是采用 webview 或者 React Native 這種方案,而對(duì)于 Android 插件化采用的技術(shù)對(duì)于 Android Framewrok 的理解要求很高,真正實(shí)現(xiàn)落地的方案都還是有難度,對(duì)于非 Android Native 開(kāi)發(fā)的人員更是有技術(shù)門(mén)檻。插件化可以很好的解決 Android 運(yùn)行的一些問(wèn)題,本文站在學(xué)習(xí)者的角度去嘗試?yán)斫獠寮降捉鉀Q了什么問(wèn)題。
插件化框架如下是主流的插件化框架之間的對(duì)比:
特性 | DynamicLoadApk | DynamicAPK | Small | DroidPlugin | VirtualAPK |
---|---|---|---|---|---|
支持四大組件 | 只支持 Activity | 只支持 Activity | 只支持 Activity | 全支持 | 全支持 |
無(wú)需在宿主 manifest 中預(yù)注冊(cè) | √ | × | √ | √ | √ |
插件可以依賴(lài)宿主 | √ | √ | √ | × | √ |
支持 PendingIntent | × | × | × | √ | √ |
Android 特性支持 | 大部分 | 大部分 | 大部分 | 幾乎全部 | 幾乎全部 |
兼容性適配 | 一般 | 一般 | 中等 | 高 | 高 |
插件構(gòu)建 | 無(wú) | 部署 aapt | Gradle 插件 | 無(wú) | Gradle 插件 |
代理模式是為一個(gè)對(duì)象提供一個(gè)代用品或占位符,以便控制對(duì)它的訪問(wèn)。使用代理可以屏蔽內(nèi)部實(shí)現(xiàn)細(xì)節(jié),后續(xù)內(nèi)部有變動(dòng)對(duì)于外部調(diào)用者來(lái)說(shuō)是封閉的,符合開(kāi)放-封閉原則。用戶可以放心地請(qǐng)求代理,他只關(guān)心是否能得到想要的結(jié)果。在任何使用本體的地方都可以替換成使用代理,從而實(shí)現(xiàn)實(shí)現(xiàn)和調(diào)用松耦合。
不用代理模式:
使用代理模式:
靜態(tài)代理例如我們有兩個(gè)接口:
// Subject1.java public interface Subject1 { void method1(); void method2(); } // Subject2.java public interface Subject2 { void method1(); void method2(); void method3(); }
我們分別實(shí)現(xiàn)這兩個(gè)接口:
// RealSubject1.java public class RealSubject1 implements Subject1 { @Override public void method1() { Logger.i(RealSubject1.class, "我是RealSubject1的方法1"); } @Override public void method2() { Logger.i(RealSubject1.class, "我是RealSubject1的方法2"); } } // RealSubject2.java public class RealSubject2 implements Subject2 { @Override public void method1() { Logger.i(RealSubject2.class, "我是RealSubject2的方法1"); } @Override public void method2() { Logger.i(RealSubject2.class, "我是RealSubject2的方法2"); } @Override public void method3() { Logger.i(RealSubject2.class, "我是RealSubject2的方法3"); } }
如果不使用代理模式,我們一般會(huì)直接實(shí)例化 RealSubject1 和 RealSubject2 類(lèi)對(duì)象。使用代理,我們一般都需要建立一個(gè)代理類(lèi)。在 Java 等語(yǔ)言中,代理和本體都需要顯式地實(shí)現(xiàn)同一個(gè)接口,一方面接口保證了它們會(huì)擁 有同樣的方法,另一方面,面向接口編程迎合依賴(lài)倒置原則,通過(guò)接口進(jìn)行向上轉(zhuǎn)型,從而避開(kāi) 編譯器的類(lèi)型檢查,代理和本體將來(lái)可以被替換使用。
/** * 靜態(tài)代理類(lèi)(為了保持行為的一致性,代理類(lèi)和委托類(lèi)通常會(huì)實(shí)現(xiàn)相同的接口) * ProxySubject1.java */ public class ProxySubject1 implements Subject1 { private Subject1 subject1; public ProxySubject1(Subject1 subject1) { this.subject1 = subject1; } @Override public void method1() { Logger.i(ProxySubject1.class, "我是代理,我會(huì)在執(zhí)行實(shí)體方法1之前先做一些預(yù)處理的工作"); subject1.method1(); } @Override public void method2() { Logger.i(ProxySubject1.class, "我是代理,我會(huì)在執(zhí)行實(shí)體方法2之前先做一些預(yù)處理的工作"); subject1.method2(); } }
使用代理后我們對(duì) RealSubject1 的操作換成對(duì) ProxySubject1 對(duì)象的操作,如下:
ProxySubject1 proxySubject1 = new ProxySubject1(new RealSubject1()); proxySubject1.method1(); proxySubject1.method2(); 結(jié)果: [ProxySubject1] : 我是代理,我會(huì)在執(zhí)行實(shí)體方法1之前先做一些預(yù)處理的工作 [RealSubject1] : 我是RealSubject1的方法1 [ProxySubject1] : 我是代理,我會(huì)在執(zhí)行實(shí)體方法2之前先做一些預(yù)處理的工作 [RealSubject1] : 我是RealSubject1的方法2 [ProxySubject2] : 我是代理,我會(huì)在執(zhí)行實(shí)體方法1之前先做一些預(yù)處理的工作 [RealSubject2] : 我是RealSubject2的方法1 [ProxySubject2] : 我是代理,我會(huì)在執(zhí)行實(shí)體方法2之前先做一些預(yù)處理的工作 [RealSubject2] : 我是RealSubject2的方法2
顯然當(dāng)我們想代理 RealSubject2 按照這種方式我們?nèi)匀恍枰⒁粋€(gè)類(lèi)去處理,這也是靜態(tài)代理的局限性。如果寫(xiě)一個(gè)代理類(lèi)就能對(duì)上面兩個(gè)都能代理就好了,動(dòng)態(tài)代理就解決了這個(gè)問(wèn)題。
動(dòng)態(tài)代理在 java 的動(dòng)態(tài)代理機(jī)制中,有兩個(gè)重要的類(lèi)或接口,一個(gè)是 InvocationHandler(Interface)、另一個(gè)則是 Proxy(Class),這一個(gè)類(lèi)和接口是實(shí)現(xiàn)我們動(dòng)態(tài)代理所必須用到的。
動(dòng)態(tài)代理的步驟:
寫(xiě)一個(gè) InvocationHandler 的實(shí)現(xiàn)類(lèi),并實(shí)現(xiàn) invoke 方法,return method.invoke(...);。
/** * @param proxy 指代我們所代理的那個(gè)真實(shí)對(duì)象 * @param method 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象 * @param args 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù) */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
每一個(gè)動(dòng)態(tài)代理類(lèi)都必須要實(shí)現(xiàn) InvocationHandler 這個(gè)接口,并且每個(gè)代理類(lèi)的實(shí)例都關(guān)聯(lián)到了一個(gè) handler,當(dāng)我們通過(guò)代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由 InvocationHandler 這個(gè)接口的 invoke 方法來(lái)進(jìn)行調(diào)用。
使用 Proxy 類(lèi)的 newProxyInstance 方法生成一個(gè)代理對(duì)象。例如: 生成 Subject1 的代理對(duì)象,注意第三個(gè)參數(shù)中要將一個(gè)實(shí)體對(duì)象傳入。
/** * @param loader 一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載 * @param interfaces 一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱(chēng)實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了 * @param h 一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上 */ public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException
例如:
Proxy.newProxyInstance( Subject1.class.getClassLoader(), new Class[] {Subject1.class}, new DynamicProxyHandler(new RealSubject1()) );
Proxy 這個(gè)類(lèi)的作用就是用來(lái)動(dòng)態(tài)創(chuàng)建一個(gè)代理對(duì)象的類(lèi),它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個(gè)方法。
使用動(dòng)態(tài)代理完成上述靜態(tài)代理中的功能:
public class DynamicProxyHandler implements InvocationHandler { private Object object; public DynamicProxyHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Logger.i(DynamicProxyHandler.class, "我正在動(dòng)態(tài)代理[" + object.getClass().getSimpleName() + "]的[" + method.getName() + "]方法"); return method.invoke(object, args); } /** * 調(diào)用Proxy.newProxyInstance即可生成一個(gè)代理對(duì)象 * * @param object * @return */ public static Object newProxyInstance(Object object) { // 傳入被代理對(duì)象的classloader實(shí)現(xiàn)的接口, 還有DynamicProxyHandler的對(duì)象即可。 return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new DynamicProxyHandler(object)); } }
動(dòng)態(tài)代理調(diào)用如下:
Subject1 dynamicProxyHandler1 = (Subject1) DynamicProxyHandler.newProxyInstance(new RealSubject1()); dynamicProxyHandler1.method1(); dynamicProxyHandler1.method2();初識(shí) Hook 機(jī)制
上述我們對(duì)一個(gè)方法的調(diào)用采用了動(dòng)態(tài)代理的辦法,如果我們自己創(chuàng)建代理對(duì)象,然后把原始對(duì)象替換為我們的代理對(duì)象,那么就可以在這個(gè)代理對(duì)象為所欲為了,修改參數(shù),替換返回值,我們稱(chēng)之為 Hook。下面我們 Hook 掉 startActivity 這個(gè)方法,使得每次調(diào)用這個(gè)方法之前輸出一條日志;當(dāng)然,這個(gè)輸入日志有點(diǎn)點(diǎn)弱,只是為了展示原理;只要你想,你想可以替換參數(shù),攔截這個(gè) startActivity 過(guò)程,使得調(diào)用它導(dǎo)致啟動(dòng)某個(gè)別的 Activity,指鹿為馬!
首先我們得找到被 Hook 的對(duì)象,我稱(chēng)之為 Hook 點(diǎn);什么樣的對(duì)象比較好 Hook 呢?自然是容易找到的對(duì)象。什么樣的對(duì)象容易找到?靜態(tài)變量和單例。在一個(gè)進(jìn)程之內(nèi),靜態(tài)變量和單例變量是相對(duì)不容易發(fā)生變化的,因此非常容易定位,而普通的對(duì)象則要么無(wú)法標(biāo)志,要么容易改變。我們根據(jù)這個(gè)原則找到所謂的 Hook 點(diǎn)。
對(duì)于 startActivity 過(guò)程有兩種方式:Context.startActivity 和 Activity.startActivity。這里暫不分析其中的區(qū)別,以 Activity.startActivity 為例說(shuō)明整個(gè)過(guò)程的調(diào)用棧。
Activity 中的 startActivity 最終都是由 startActivityForResult 來(lái)實(shí)現(xiàn)的。
Activity#startActivityForResult:
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { // 一般的 Activity 其 mParent 為 null,mParent 常用在 ActivityGroup 中,ActivityGroup 已廢棄 if (mParent == null) { options = transferSpringboardActivityOptions(options); // 這里會(huì)啟動(dòng)新的Activity,核心功能都在 mMainThread.getApplicationThread() 中完成 Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); if (ar != null) { mMainThread.sendActivityResult( mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } if (requestCode >= 0) { mStartedActivity = true; } cancelInputsAndStartExitTransition(options); } else { if (options != null) { mParent.startActivityFromChild(this, intent, requestCode, options); } else { // Note we want to go through this method for compatibility with // existing applications that may have overridden it. mParent.startActivityFromChild(this, intent, requestCode); } } }
可以發(fā)現(xiàn),真正打開(kāi) activity 的實(shí)現(xiàn)在 Instrumentation 的 execStartActivity 方法中。
Instrumentation#execStartActivity:
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { // 核心功能在這個(gè)whoThread中完成,其內(nèi)部scheduleLaunchActivity方法用于完成activity的打開(kāi) 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= 0 ? am.getResult() : null; } break; } } } } try { intent.migrateExtraStreamToClipData(); intent.prepareToLeaveProcess(who); // 這里才是真正打開(kāi) Activity 的地方,核心功能在 whoThread 中完成。 int result = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); // 這個(gè)方法是專(zhuān)門(mén)拋異常的,它會(huì)對(duì)結(jié)果進(jìn)行檢查,如果無(wú)法打開(kāi)activity, // 則拋出諸如ActivityNotFoundException類(lèi)似的各種異常 checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } return null; }
如果我們想深入了解 Activity 啟動(dòng)過(guò)程我們需要接著 Android 源碼看下去,但是對(duì)于本文中我們初步了解 Hook 機(jī)制足以。
我們的目的是替換掉系統(tǒng)默認(rèn)邏輯,對(duì)于 Activity#startActivityForResult 的方法里面核心邏輯就是 mInstrumentation 屬性的 execStartActivity 方法,而這里的 mInstrumentation 屬性在 Activity 類(lèi)中恰好是一個(gè)單例,在 Activity 類(lèi)的 attach 方法里面被賦值,我們可以在 attach 之后使用反射機(jī)制對(duì) mInstrumentation 屬性進(jìn)行重新賦值。attach() 方法調(diào)用完成后,就自然而然的調(diào)用了 Activity 的 onCreate() 方法了。
我們需要修改 mInstrumentation 這個(gè)字段為我們的代理對(duì)象,我們使用靜態(tài)代理實(shí)現(xiàn)這個(gè)代理對(duì)象。這里我們使用 EvilInstrumentation 作為代理對(duì)象。
public class EvilInstrumentation extends Instrumentation { private Instrumentation instrumentation; public EvilInstrumentation(Instrumentation instrumentation) { this.instrumentation = instrumentation; } public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { StringBuilder sb = new StringBuilder(); sb.append("who = [").append(who).append("], ") .append("contextThread = [").append(contextThread).append("], ") .append("token = [").append(token).append("], ") .append("target = [").append(target).append("], ") .append("intent = [").append(intent).append("], ") .append("requestCode = [").append(requestCode).append("], ") .append("options = [").append(options).append("]");; Logger.i(EvilInstrumentation.class, "執(zhí)行了startActivity, 參數(shù)如下: " + sb.toString()); try { Method execStartActivity = Instrumentation.class.getDeclaredMethod( "execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); return (ActivityResult) execStartActivity.invoke(instrumentation, who, contextThread, token, target, intent, requestCode, options); } catch (Exception e) { e.printStackTrace(); } return null; } }
采用反射直接修改 Activity 中的 mInstrumentation 屬性,從而實(shí)現(xiàn)偷梁換柱——用代理對(duì)象替換原始對(duì)象。
// 拿到原始的 mInstrumentation字段 Field mInstrumentationField = Activity.class.getDeclaredField("mInstrumentation"); mInstrumentationField.setAccessible(true); // 創(chuàng)建代理對(duì)象 Instrumentation originalInstrumentation = (Instrumentation) mInstrumentationField.get(activity); mInstrumentationField.set(activity, new EvilInstrumentation(originalInstrumentation));
這段 Hook 的邏輯放在 Activity 的 onCreate 里面即可生效。
對(duì)于 Context 類(lèi)的 startActivity 方法的 Hook 實(shí)現(xiàn)可以參考 weishu 大神的 Android 插件化原理解析——Hook 機(jī)制之動(dòng)態(tài)代理,本文也是基于 weishu 大神的文章在學(xué)習(xí)過(guò)程記錄的內(nèi)容。
Activity 啟動(dòng)過(guò)程上述例子中我們只是完成了一個(gè)最基礎(chǔ)的 Hook 功能,然而大部分插件化框架提供了十分豐富的功能,例如:插件化支持首先要解決的一點(diǎn)就是插件里的 Activity 并未在宿主程序的 AndroidMainfest.xml 注冊(cè)。常規(guī)方法肯定無(wú)法直接啟動(dòng)插件的 Activity,這個(gè)時(shí)候就需要去了解 Activity 的啟動(dòng)流程。
完整的流程如下:
注: 可以在 http://androidxref.com/ 在線查看 Android 源碼。
上圖列出的是啟動(dòng)一個(gè) Activity 的主要過(guò)程,具體步驟如下:
Activity 調(diào)用 startActivity,實(shí)際會(huì)調(diào)用 Instrumentation 類(lèi)的 execStartActivity 方法,Instrumentation 是系統(tǒng)用來(lái)監(jiān)控 Activity 運(yùn)行的一個(gè)類(lèi),Activity 的整個(gè)生命周期都有它的影子。
通過(guò)跨進(jìn)程的 Binder 調(diào)用,進(jìn)入到 ActivityManagerService 中,其內(nèi)部會(huì)處理 Activity 棧。之后又通過(guò)跨進(jìn)程調(diào)用進(jìn)入到需要調(diào)用的 Activity 所在的進(jìn)程中。
ApplicationThread 是一個(gè) Binder 對(duì)象,其運(yùn)行在 Binder 線程池中,內(nèi)部包含一個(gè) H 類(lèi),該類(lèi)繼承于類(lèi) Handler。ApplicationThread 將啟動(dòng)需要調(diào)用的 Activity 的信息通過(guò) H 對(duì)象發(fā)送給主線程。
主線程拿到需要調(diào)用的 Activity 的信息后,調(diào)用 Instrumentation 類(lèi)的 newActivity 方法,其內(nèi)通過(guò) ClassLoader 創(chuàng)建 Activity 實(shí)例。
下面介紹如何通過(guò) hook 的方式啟動(dòng)插件中的 Activity,需要解決以下兩個(gè)問(wèn)題:
插件中的 Activity 沒(méi)有在 AndroidManifest 中注冊(cè),如何繞過(guò)檢測(cè)。
如何構(gòu)造 Activity 實(shí)例,同步生命周期。
我們這里使用最簡(jiǎn)單的一種實(shí)現(xiàn)方式:先在 Manifest 中預(yù)埋 StubActivity,啟動(dòng)時(shí) hook 上圖第 1 步,將 Intent 替換成 StubActivity。
// StubActivity.java public class StubActivity extends Activity { public static final String TARGET_COMPONENT = "TARGET_COMPONENT"; }
我們上面在 EvilInstrumentation 類(lèi)里面實(shí)現(xiàn)了 execStartActivity 方法,現(xiàn)在我們?cè)谶@里再加一些額外的邏輯。
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { StringBuilder sb = new StringBuilder(); sb.append("who = [").append(who).append("], ") .append("contextThread = [").append(contextThread).append("], ") .append("token = [").append(token).append("], ") .append("target = [").append(target).append("], ") .append("intent = [").append(intent).append("], ") .append("requestCode = [").append(requestCode).append("], ") .append("options = [").append(options).append("]");; Logger.i(EvilInstrumentation.class, "執(zhí)行了startActivity, 參數(shù)如下: " + sb.toString()); // 在此處先將 intent 原本的 Component 保存起來(lái), 然后創(chuàng)建一個(gè)新的 intent。 // 使用 StubActivity 并替換掉原本的 Activity, 以達(dá)通過(guò) AMS 驗(yàn)證的目的,然后等 AMS 驗(yàn)證通過(guò)后再將其還原。 Intent replaceIntent = new Intent(target, StubActivity.class); replaceIntent.putExtra(StubActivity.TARGET_COMPONENT, intent); intent = replaceIntent; try { Method execStartActivity = Instrumentation.class.getDeclaredMethod( "execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); return (ActivityResult) execStartActivity.invoke(instrumentation, who, contextThread, token, target, intent, requestCode, options); } catch (Exception e) { e.printStackTrace(); } return null; }
通過(guò)這種"移花接木"的方式繞過(guò) AMS 驗(yàn)證,但是這里我們并沒(méi)有完成對(duì)我們?cè)拘枰嬲蜷_(kāi)的 Activity 的創(chuàng)建。這里我們需要監(jiān)聽(tīng) Activity 的創(chuàng)建過(guò)程,然后在適當(dāng)?shù)倪m合將原本需要打開(kāi)的 Activity 還原回來(lái)。
在 ActivityThread 類(lèi)中有一個(gè)重要的消息處理的方法 sendMessage。
2644 private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { 2645 if (DEBUG_MESSAGES) Slog.v( 2646 TAG, "SCHEDULE " + what + " " + mH.codeToString(what) 2647 + ": " + arg1 + " / " + obj); 2648 Message msg = Message.obtain(); 2649 msg.what = what; 2650 msg.obj = obj; 2651 msg.arg1 = arg1; 2652 msg.arg2 = arg2; 2653 if (async) { 2654 msg.setAsynchronous(true); 2655 } 2656 mH.sendMessage(msg); 2657 }
最終都會(huì)落實(shí)到 mH.sendMessage(msg); 的調(diào)用,繼續(xù)追蹤這個(gè) mH 對(duì)象,我們會(huì)發(fā)現(xiàn)是 H 對(duì)象的實(shí)例化對(duì)象。
final H mH = new H();
private class H extends Handler { public void handleMessage(Message msg) { 1585 if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); 1586 switch (msg.what) { 1587 case LAUNCH_ACTIVITY: { 1588 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); 1589 final ActivityClientRecord r = (ActivityClientRecord) msg.obj; 1590 1591 r.packageInfo = getPackageInfoNoCheck( 1592 r.activityInfo.applicationInfo, r.compatInfo); 1593 handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); 1594 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 1595 } break; ... } }
我們知道 Handler 消息機(jī)制用于同進(jìn)程的線程間通信, Handler 是工作線程向 UI 主線程發(fā)送消息,工作線程通過(guò) mHandler 向其成員變量 MessageQueue 中添加新 Message,主線程一直處于 loop() 方法內(nèi),當(dāng)收到新的 Message 時(shí)按照一定規(guī)則分發(fā)給相應(yīng)的 handleMessage() 方法來(lái)處理。
類(lèi)似于對(duì)上述 mInstrumentation 實(shí)例化對(duì)象 hook 一樣,這里我們可以對(duì) mH 對(duì)象進(jìn)行 hook。
/** * 將替換的activity在此時(shí)還原回來(lái) */ public static void doHandlerHook() { try { Class> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThread = activityThreadClass.getDeclaredMethod("currentActivityThread"); Object activityThread = currentActivityThread.invoke(null); Field mHField = activityThreadClass.getDeclaredField("mH"); mHField.setAccessible(true); Handler mH = (Handler) mHField.get(activityThread); Field mCallbackField = Handler.class.getDeclaredField("mCallback"); mCallbackField.setAccessible(true); mCallbackField.set(mH, new ActivityThreadHandlerCallback(mH)); } catch (Exception e) { e.printStackTrace(); } }
對(duì)于 Handler.Callback 的 hook 實(shí)現(xiàn)如下:
public class ActivityThreadHandlerCallback implements Handler.Callback { private Handler mBaseHandler; public ActivityThreadHandlerCallback(Handler mBaseHandler) { this.mBaseHandler = mBaseHandler; } @Override public boolean handleMessage(Message msg) { Logger.i(ActivityThreadHandlerCallback.class, "接受到消息了msg:" + msg); if (msg.what == 100) { try { Object obj = msg.obj; Field intentField = obj.getClass().getDeclaredField("intent"); intentField.setAccessible(true); Intent intent = (Intent) intentField.get(obj); Intent targetIntent = intent.getParcelableExtra(StubActivity.TARGET_COMPONENT); intent.setComponent(targetIntent.getComponent()); Log.e("intentField", targetIntent.toString()); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } mBaseHandler.handleMessage(msg); return true; } }
我們?cè)O(shè)置在 handleMessage 里面還原我們最開(kāi)始替換的 Activity,至此我們就實(shí)現(xiàn)了對(duì)于 startActivity 的完整 hook,但是這個(gè)過(guò)程中仍然存在很多問(wèn)題,我們需要進(jìn)一步去深入探索才能去理解和更好實(shí)現(xiàn)插件化框架的內(nèi)容。
學(xué)習(xí)案例本文學(xué)習(xí)案例地址:android-plugin-framework
參考Android 博客周刊專(zhuān)題之#插件化開(kāi)發(fā)#
VirtualAPK Wiki
DroidPlugin Wiki
understand-plugin-framework
Android 插件化原理解析——Hook 機(jī)制之動(dòng)態(tài)代理
Android 源碼分析-Activity 的啟動(dòng)過(guò)程
APP 的啟動(dòng)過(guò)程
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/71647.html
閱讀 3453·2021-11-04 16:10
閱讀 3962·2021-09-29 09:43
閱讀 2780·2021-09-24 10:24
閱讀 3649·2021-09-01 10:46
閱讀 2572·2019-08-30 15:54
閱讀 680·2019-08-30 13:19
閱讀 3304·2019-08-29 17:19
閱讀 1125·2019-08-29 16:40