在Java平臺要作到動態運行模塊、熱插拔可使用ClassLoader
技術進行動態類加載,好比普遍使用的OSGi
技術。在Android上固然也可使用動態加載技術,可是僅僅把類加載進來就足夠了嗎?Activity
,Service
等組件是有生命週期的,它們統一由系統服務AMS
管理;使用ClassLoader
能夠從插件中建立Activity對象,可是,一個沒有生命週期的Activity對象有什麼用?因此在Android系統上,僅僅完成動態類加載是不夠的;咱們須要想辦法把咱們加載進來的Activity等組件交給系統管理,讓AMS
賦予組件生命週期;這樣纔算是一個有血有肉的完善的插件化方案。java
接下來的系列文章會講述 DroidPlugin對於Android四大組件的處理方式,咱們且看它如何採用Hook技術坑蒙拐騙把系統玩弄於股掌之中,最終賦予Activity,Service等組件生命週期,完成借屍還魂的。android
首先,咱們來看看DroidPlugin對於Activity
組件的處理方式。git
閱讀本文以前,能夠先clone一份 understand-plugin-framework,參考此項目的intercept-activity模塊。另外,若是對於Hook技術不甚瞭解,請先查閱我以前的文章:github
讀到這裏,或許有部分讀者以爲疑惑了,啓動Activity不就是一個startActivity
的事嗎,有這麼神祕兮兮的?app
啓動Activity確實很是簡單,可是Android卻有一個限制:必須在AndroidManifest.xml中顯示聲明使用的Activity;我相信讀者確定會遇到下面這種異常:框架
1 2 3 |
03-18 15:29:56.074 20709-20709/com.weishu.intercept_activity.app E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.weishu.intercept_activity.app, PID: 20709 android.content.ActivityNotFoundException: Unable to find explicit activity class {com.weishu.intercept_activity.app/com.weishu.intercept_activity.app.TargetActivity}; have you declared this activity in your AndroidManifest.xml? |
『必須在AndroidManifest.xml中顯示聲明使用的Activity』這個硬性要求很大程度上限制了插件系統的發揮:假設咱們須要啓動一個插件的Activity,插件使用的Activity是沒法預知的,這樣確定也不會在Manifest文件中聲明;若是插件新添加一個Activity,主程序的AndroidManifest.xml就須要更新;既然雙方都須要修改升級,何須要使用插件呢?這已經違背了動態加載的初衷:不修改插件框架而動態擴展功能。ide
能不能想辦法繞過這個限制呢?函數
一籌莫展啊,怎麼辦?借刀殺人偷樑換柱無中生有以逸待勞乘火打劫瞞天過海…等等!偷樑換柱瞞天過海?貌似能夠一試。學習
咱們能夠耍個障眼法:既然AndroidManifest文件中必須聲明,那麼我就聲明一個(或者有限個)替身Activity好了,當須要啓動插件的某個Activity的時候,先讓系統覺得啓動的是AndroidManifest中聲明的那個替身,暫時騙過系統;而後到合適的時候又替換回咱們須要啓動的真正的Activity;所謂瞞天過海,莫過如此!this
如今有了方案了,可是該如何作呢?兵書又說,知己知彼百戰不殆!若是連Activity的啓動過程都不熟悉,怎麼完成這個瞞天過海的過程?
啓動Activity很是簡單,一個startActivity
就完事了;那麼在這個簡單調用的背後發生了什麼呢?Look the fucking source code!
關於Activity 的啓動過程,也不是三言兩語能解釋清楚的,若是按照源碼一步一步走下來,插件化系列文章就不用寫了;因此這裏我就給出一個大體流程,只列出關鍵的調用點(以Android 6.0源碼爲例);若是讀者但願更詳細的講解,能夠參考老羅的 Android應用程序的Activity啓動過程簡要介紹和學習計劃
首先是Activity類的startActivity
方法:
1 2 3 |
public void startActivity(Intent intent) { startActivity(intent, null); } |
跟着這個方法一步一步跟蹤,會發現它最後在startActivityForResult
裏面調用了Instrument對象的execStartActivity
方法;接着在這個函數裏面調用了ActivityManagerNative類的startActivity
方法;這個過程在前文已經反覆舉例講解了,咱們知道接下來會經過Binder IPC到AMS
所在進程調用AMS
的startActivity
方法;Android系統的組件生命週期管理就是在AMS
裏面完成的,那麼在AMS
裏面到底作了什麼呢?
ActivityManagerService的startActivity
方法以下:
1 2 3 4 5 6 7 8 |
public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile, ParcelFileDescriptor profileFd, Bundle options) { return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId()); } |
很簡單,直接調用了startActivityAsUser
這個方法;接着是ActivityStackSupervisor
類的startActivityMayWait
方法。這個ActivityStackSupervisor類究竟是個啥?若是仔細查閱,低版本的Android源碼上是沒有這個類的;後來AMS的代碼進行了部分重構,關於Activity棧管理的部分單獨提取出來成爲了ActivityStackSupervisor
類;好了,繼續看代碼。
startActivityMayWait這個方法前面對參數進行了一系列處理,咱們須要知道的是,在這個方法內部對傳進來的Intent進行了解析,並嘗試從中取出關於啓動Activity的信息。
而後這個方法調用了startActivityLocked方法;在startActivityLocked方法內部進行了一系列重要的檢查:好比權限檢查,Activity的exported屬性檢查等等;咱們上文所述的,啓動沒有在Manifestfest中顯示聲明的Activity拋異常也是這裏發生的:
1 2 3 4 5 |
if (err == ActivityManager.START_SUCCESS && aInfo == null) { // We couldn't find the specific class specified in the Intent. // Also the end of the line. err = ActivityManager.START_CLASS_NOT_FOUND; } |
這裏返回ActivityManager.START_CLASS_NOT_FOUND以後,在Instrument的execStartActivity返回以後會檢查這個值,而後跑出異常:
1 2 3 4 5 6 |
case ActivityManager.START_CLASS_NOT_FOUND: if (intent instanceof Intent && ((Intent)intent).getComponent() != null) throw new ActivityNotFoundException( "Unable to find explicit activity class " + ((Intent)intent).getComponent().toShortString() + "; have you declared this activity in your AndroidManifest.xml?"); |
源碼看到這裏,咱們已經確認了『必須在AndroidManifest.xml中顯示聲明使用的Activity』的緣由;然而這個校檢過程發生在AMS
所在的進程system_server
,咱們沒有辦法篡改,只能另尋他路。
OK,咱們繼續跟蹤源碼;在startActivityLocked以後處理的都是Activity任務棧相關內容,這一系列ActivityStack和ActivityStackSupervisor糾纏不清的調用看下圖就明白了;不明白也不要緊: D 目前用處不大。
這一系列調用最終到達了ActivityStackSupervisor的realStartActivityLocked方法;人如其名,這個方法開始了真正的「啓動Activity」:它調用了ApplicationThread的scheduleLaunchActivity方法,開始了真正的Activity對象建立以及啓動過程。
這個ApplicationThread是什麼,是一個線程嗎?與ActivityThread有什麼區別和聯繫?
不要被名字迷惑了,這個ApplicationThread其實是一個Binder對象,是App所在的進程與AMS所在進程system_server通訊的橋樑;在Activity啓動的過程當中,App進程會頻繁地與AMS進程進行通訊:
App進程與AMS進程的通訊過程如圖所示:
App進程內部的ApplicationThread server端內部有本身的Binder線程池,它與App主線程的通訊經過Handler完成,這個Handler存在於ActivityThread類,它的名字很簡單就叫H
,這一點咱們接下來就會講到。
如今咱們明白了這個ApplicationThread究竟是個什麼東西,接上文繼續跟蹤Activity的啓動過程;咱們查看ApplicationThread的scheduleLaunchActivity
方法,這個方法很簡單,就是包裝了參數最終使用Handler發了一個消息。
正如剛剛所說,ApplicationThread所在的Binder服務端使用Handler與主線程進行通訊,這裏的scheduleLaunchActivity方法直接把啓動Activity的任務經過一個消息轉發給了主線程;咱們查看Handler類對於這個消息的處理:
1 2 3 4 5 6 7 |
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
能夠看到,這裏直接調用了ActivityThread的handleLaunchActivity方法,在這個方法內部有一句很是重要:
1 |
Activity a = performLaunchActivity(r, customIntent); |
繞了這麼多彎,咱們的Activity終於被建立出來了!繼續跟蹤這個performLaunchActivity方法看看發生了什麼;因爲這個方法較長,我就不貼代碼了,讀者能夠自行查閱;要指出的是,這個方法作了兩件很重要的事情:
1 2 3 4 5 |
java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); |
1 2 3 4 5 6 7 8 |
Application app = r.packageInfo.makeApplication(false, mInstrumentation); // ... 省略 if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); } |
Activity的啓動過程到這裏就結束了,可能讀者仍是以爲迷惑:不就是調用了一系列方法嗎?具體作了什麼仍是不太清楚,並且爲何Android要這麼設計?
方法調用鏈再長也木有關係,有兩點須要明白:
Application, Activity
除了表明Android應用層一般所表明的「組件」以外,它們其實都是普通的Java對象,也是須要被構造函數構造出來的對象的;在這個過程當中,咱們明白了這些對象究竟是如何被建立的。AMS
正如名字所說,管理全部的「活動」,整個系統的Activity堆棧,Activity生命週期回調都是由AMS所在的系統進程system_server幫開發者完成的;Android的Framework層幫忙完成了諸如生命週期管理等繁瑣複雜的過程,簡化了應用層的開發。經過上文的分析,咱們已經對Activity的啓動過程瞭如指掌了;就讓咱們乾點壞事吧 :D
對與『必須在AndroidManifest.xml中顯示聲明使用的Activity』這個問題,上文給出了思路——瞞天過海;咱們能夠在AndroidManifest.xml裏面聲明一個替身Activity,而後在合適的時候把這個假的替換成咱們真正須要啓動的Activity就OK了。
那麼問題來了,『合適的時候』究竟是何時?在前文Hook機制之動態代理中咱們提到過Hook過程最重要的一步是尋找Hook點;若是是在同一個進程,startActivity
到Activity真正啓動起來這麼長的調用鏈,咱們隨便找個地方Hook掉就完事兒了;可是問題木有這麼簡單。
Activity啓動過程當中不少重要的操做(正如上文分析的『必須在AndroidManifest.xml中顯式聲明要啓動的Activity』)都不是在App進程裏面執行的,而是在AMS所在的系統進程system_server完成,因爲進程隔離的存在,咱們對別的進程無能爲力;因此這個Hook點就須要花點心思了。
這時候Activity啓動過程的知識就派上用場了;雖然整個啓動過程很是複雜,但其實一張圖就能總結:
先從App進程調用startActivity
;而後經過IPC調用進入系統進程system_server,完成Activity管理以及一些校檢工做,最後又回到了APP進程完成真正的Activioty對象建立。
因爲這個檢驗過程是在AMS進程完成的,咱們對system_server進程裏面的操做無能爲力,只有在咱們APP進程裏面執行的過程纔是有可能被Hook掉的,也就是第一步和第三步;具體應該怎麼辦呢?
既然須要一個顯式聲明的Activity,那就聲明一個!能夠在第一步僞裝啓動一個已經在AndroidManifest.xml裏面聲明過的替身Activity,讓這個Activity進入AMS進程接受檢驗;最後在第三步的時候換成咱們真正須要啓動的Activity;這樣就成功欺騙了AMS進程,瞞天過海!
說到這裏,是否是有點小激動呢?咱們寫個demo驗證一下:『啓動一個並無在AndroidManifest.xml中顯示聲明的Activity』
具體來講,咱們打算實現以下功能:在MainActivity中啓動一個並無在AndroidManifest.xml中聲明的TargetActivity;按照上文分析,咱們須要聲明一個替身Activity,咱們叫它StubActivity;
那麼,咱們的AndroidManifest.xml以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.weishu.intercept_activity.app"> <application android:allowBackup="true" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" > <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- 替身Activity, 用來欺騙AMS --> <activity android:name=".StubActivity"/> </application> </manifest> |
OK,那麼咱們啓動TargetActivity很簡單,就是個startActivity
調用的事:
1 |
startActivity(new Intent(MainActivity.this, TargetActivity.class)); |
若是你直接這麼運行,確定會直接拋出ActivityNotFoundException而後直接退出;咱們接下來要作的就是讓這個調用成功啓動TargetActivity。
因爲AMS
進程會對Activity作顯式聲明驗證,所以在
啓動Activity的控制權轉移到AMS
進程以前,咱們須要想辦法臨時把TargetActivity替換成替身StubActivity;在這之間有很長的一段調用鏈,咱們能夠輕鬆Hook掉;選擇什麼地方Hook是一個很自由的事情,可是Hook的步驟越後越可靠——Hook得越早,後面的調用就越複雜,越容易出錯。
咱們能夠選擇在進入AMS
進程的入口進行Hook,具體來講也就是Hook AMS
在本進程的代理對象ActivityManagerNative。若是你不知道如何Hook掉這個AMS的代理對象,請查閱我以前的文章Hook機制之AMS&PMS
咱們Hook掉ActivityManagerNative對於startActivity方法的調用,替換掉交給AMS的intent對象,將裏面的TargetActivity的暫時替換成已經聲明好的替身StubActivity;這種Hook方式 前文 講述的很詳細,不贅述;替換的關鍵代碼以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
if ("startActivity".equals(method.getName())) { // 只攔截這個方法 // 替換參數, 任你所爲;甚至替換原始Activity啓動別的Activity偷樑換柱 // API 23: // public final Activity startActivityNow(Activity parent, String id, // Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state, // Activity.NonConfigurationInstances lastNonConfigurationInstances) { // 找到參數裏面的第一個Intent 對象 Intent raw; int index = 0; for (int i = 0; i < args.length; i++) { if (args[i] instanceof Intent) { index = i; break; } } raw = (Intent) args[index]; Intent newIntent = new Intent(); // 這裏包名直接寫死,若是再插件裏,不一樣的插件有不一樣的包 傳遞插件的包名便可 String targetPackage = "com.weishu.intercept_activity.app"; // 這裏咱們把啓動的Activity臨時替換爲 StubActivity ComponentName componentName = new ComponentName(targetPackage, StubActivity.class.getCanonicalName()); newIntent.setComponent(componentName); // 把咱們原始要啓動的TargetActivity先存起來 newIntent.putExtra(HookHelper.EXTRA_TARGET_INTENT, raw); // 替換掉Intent, 達到欺騙AMS的目的 args[index] = newIntent; Log.d(TAG, "hook success"); return method.invoke(mBase, args); } return method.invoke(mBase, args); |
經過這個替換過程,在ActivityManagerNative的startActivity調用以後,system_server端收到Binder驅動的消息,開始執行ActivityManagerService裏面真正的startActivity
方法;這時候AMS看到的intent
參數裏面的組件已是StubActivity
了,所以能夠成功繞過檢查,這時候若是不作後面的Hook,直接調用
1 |
startActivity(new Intent(MainActivity.this, TargetActivity.class)); |
也不會出現上文的ActivityNotFoundException
行百里者半九十。如今咱們的startActivity
啓動一個沒有顯式聲明的Activity已經不會拋異常了,可是要真正正確地把TargetActivity啓動起來,還有一些事情要作。其中最重要的一點是,咱們用替身StubActivity臨時換了TargetActivity,確定須要在『合適的』時候替換回來;接下來咱們就完成這個過程。
在AMS進程裏面咱們是沒有辦法換回來的,所以咱們要等AMS把控制權交給App所在進程,也就是上面那個『Activity啓動過程簡圖』的第三步。AMS進程轉移到App進程也是經過Binder調用完成的,承載這個功能的Binder對象是IApplicationThread;在App進程它是Server端,在Server端接受Binder遠程調用的是Binder線程池,Binder線程池經過Handler將消息轉發給App的主線程;(我這裏不厭其煩地敘述Binder調用過程,但願讀者不要反感,其一加深印象,其二懂Binder真的很重要)咱們能夠在這個Handler裏面將替身恢復成真身。
這裏不打算講述Handler 的原理,咱們簡單看一下Handler是如何處理接收到的Message的,若是咱們能攔截這個Message的接收過程,就有可能完成替身恢復工做;Handler類的dispathMesage
以下:
1 2 3 4 5 6 7 8 9 10 11 12 |
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } |
從這個方法能夠看出來,Handler類消息分發的過程以下:
mCallback
存在,那麼首先執行這個mCallback
回調;mCallback
的回調返回true
,那麼表示消息已經成功處理;直接結束。mCallback
的回調返回false
,那麼表示消息沒有處理完畢,會繼續使用Handler類的handleMessage
方法處理消息。那麼,ActivityThread中的Handler類H
是如何實現的呢?H
的部分源碼以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case RELAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; handleRelaunchActivity(r); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // 如下略 } } |
能夠看到H
類僅僅重載了handleMessage
方法;經過dispathMessage的消息分發過程得知,咱們能夠攔截這一過程:把這個H
類的mCallback
替換爲咱們的自定義實現,這樣dispathMessage
就會首先使用這個自定義的mCallback
,而後看狀況使用H
重載的handleMessage
。
這個Handler.Callback
是一個接口,咱們可使用動態代理或者普通代理完成Hook,這裏咱們使用普通的靜態代理方式;建立一個自定義的Callback類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
/* package */ class ActivityThreadHandlerCallback implements Handler.Callback { Handler mBase; public ActivityThreadHandlerCallback(Handler base) { mBase = base; } @Override public boolean handleMessage(Message msg) { switch (msg.what) { // ActivityThread裏面 "LAUNCH_ACTIVITY" 這個字段的值是100 // 原本使用反射的方式獲取最好, 這裏爲了簡便直接使用硬編碼 case 100: handleLaunchActivity(msg); break; } mBase.handleMessage(msg); return true; } private void handleLaunchActivity(Message msg) { // 這裏簡單起見,直接取出TargetActivity; Object obj = msg.obj; // 根據源碼: // 這個對象是 ActivityClientRecord 類型 // 咱們修改它的intent字段爲咱們原來保存的便可. /* switch (msg.what) { / case LAUNCH_ACTIVITY: { / Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); / final ActivityClientRecord r = (ActivityClientRecord) msg.obj; / / r.packageInfo = getPackageInfoNoCheck( / r.activityInfo.applicationInfo, r.compatInfo); / handleLaunchActivity(r, null); */ try { // 把替身恢復成真身 Field intent = obj.getClass().getDeclaredField("intent"); intent.setAccessible(true); Intent raw = (Intent) intent.get(obj); Intent target = raw.getParcelableExtra(HookHelper.EXTRA_TARGET_INTENT); raw.setComponent(target.getComponent()); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } |
這個Callback類的使命很簡單:把替身StubActivity恢復成真身TargetActivity;有了這個自定義的Callback以後咱們須要把ActivityThread裏面處理消息的Handler類H
的的mCallback
修改成自定義callback類的對象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// 先獲取到當前的ActivityThread對象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); currentActivityThreadField.setAccessible(true); Object currentActivityThread = currentActivityThreadField.get(null); // 因爲ActivityThread一個進程只有一個,咱們獲取這個對象的mH Field mHField = activityThreadClass.getDeclaredField("mH"); mHField.setAccessible(true); Handler mH = (Handler) mHField.get(currentActivityThread); // 設置它的回調, 根據源碼: // 咱們本身給他設置一個回調,就會替代以前的回調; // public void dispatchMessage(Message msg) { // if (msg.callback != null) { // handleCallback(msg); // } else { // if (mCallback != null) { // if (mCallback.handleMessage(msg)) { // return; // } // } // handleMessage(msg); // } // } Field mCallBackField = Handler.class.getDeclaredField("mCallback"); mCallBackField.setAccessible(true); mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH)); |
到這裏,咱們已經成功地繞過AMS
,完成了『啓動沒有在AndroidManifest.xml中顯式聲明的Activity』的過程;瞞天過海,這種玩弄系統與股掌之中的快感大家能體會到嗎?
雖然咱們完成了『啓動沒有在AndroidManifest.xml中顯式聲明的Activity 』,可是啓動的TargetActivity是否有本身的生命週期呢,咱們還須要額外的處理過程嗎?
實際上TargetActivity已是一個有血有肉的Activity了:它具備本身正常的生命週期;能夠運行Demo代碼驗證一下。
這個過程是如何完成的呢?咱們以onDestroy
爲例簡要分析一下:
從Activity的
finish
方法開始跟蹤,最終會經過ActivityManagerNative到AMS
而後接着經過ApplicationThread到ActivityThread,而後經過H
轉發消息到ActivityThread的handleDestroyActivity,接着這個方法把任務交給performDestroyActivity完成。
在真正分析這個方法以前,須要說明一點的是:不知讀者是否感覺獲得,App進程與AMS
交互幾乎都是這麼一種模式,幾個角色 ActivityManagerNative, ApplicationThread, ActivityThread以及Handler類H
分工明確,讀者能夠按照這幾個角色的功能分析AMS
的任何調用過程,屢試不爽;這也是個人初衷——但願分析插件框架的過程當中能幫助深刻理解Android Framework。
好了繼續分析performDestroyActivity,關鍵代碼以下:
1 2 3 4 5 |
ActivityClientRecord r = mActivities.get(token); // ...略 mInstrumentation.callActivityOnDestroy(r.activity); |
這裏經過mActivities
拿到了一個ActivityClientRecord,而後直接把這個record裏面的Activity交給Instrument類完成了onDestroy的調用。
在咱們這個demo的場景下,r.activity是TargetActivity仍是StubActivity?按理說,因爲咱們欺騙了AMS
,AMS
應該只知道StubActivity
的存在,它壓根兒就不知道TargetActivity是什麼,爲何它能正確完成對TargetActivity生命週期的回調呢?
一切的祕密在token
裏面。AMS
與ActivityThread
之間對於Activity的生命週期的交互,並無直接使用Activity對象進行交互,而是使用一個token來標識,這個token是binder對象,所以能夠方便地跨進程傳遞。Activity裏面有一個成員變量mToken
表明的就是它,token能夠惟一地標識一個Activity對象,它在Activity的attach
方法裏面初始化;
在AMS
處理Activity的任務棧的時候,使用這個token標記Activity,所以在咱們的demo裏面,AMS
進程裏面的token對應的是StubActivity,也就是AMS
還在傻乎乎地操做StubActivity(關於這一點,你能夠dump出任務棧的信息,能夠觀察到dump出的確實是StubActivity)。可是在咱們App進程裏面,token對應的倒是TargetActivity!所以,在ActivityThread執行回調的時候,能正確地回調到TargetActivity相應的方法。
爲何App進程裏面,token對應的是TargetActivity呢?
回到代碼,ActivityClientRecord是在mActivities
裏面取出來的,確實是根據token取;那麼這個token是何時添加進去的呢?咱們看performLaunchActivity就完成明白了:它經過classloader加載了TargetActivity,而後完成一切操做以後把這個activity添加進了mActivities
!另外,在這個方法裏面咱們還能看到對Ativityattach
方法的調用,它傳遞給了新建立的Activity一個token對象,而這個token是在ActivityClientRecord構造函數裏面初始化的。
至此咱們已經能夠確認,經過這種方式啓動的Activity有它本身完整而獨立的生命週期!
本文講述了『啓動一個並無在AndroidManifest.xml中顯示聲明的Activity』的解決辦法,咱們成功地繞過了Android的這個限制,這個是插件Activity管理技術的基礎;可是要作到啓動一個插件Activity問題遠沒有這麼簡單。
首先,在Android中,Activity有不一樣的啓動模式;咱們聲明瞭一個替身StubActivity,確定沒有知足全部的要求;所以,咱們須要在AndroidManifest.xml中聲明一系列的有不一樣launchMode的Activity,還須要完成替身與真正Activity launchMode的匹配過程;這樣才能完成啓動各類類型Activity的需求,關於這一點,在 DroidPlugin 的com.morgoo.droidplugin.stub包下面能夠找到。
另外,每啓動一個插件的Activity都須要一個StubActivity,可是AndroidManifest.xml中確定只能聲明有限個,若是一直startActivity
而不finish的話,那麼理論上就須要無限個StubActivity;這個問題該如何解決呢?事實上,這個問題在技術上沒有好的解決辦法。可是,若是你的App startActivity了十幾回,而沒有finish任何一個Activity,這樣在Activity的回退棧裏面有十幾個Activity,用戶難道按back十幾回回到主頁嗎?有這種需求說明你的產品設計有問題;一個App一級頁面,二級頁面..到五六級的頁面已經影響體驗了,因此,每種LauchMode聲明十個StubActivity絕對能知足需求了。
最後,在本文所述例子中,TargetActivity與StubActivity存在於同一個Apk,所以系統的ClassLoader可以成功加載並建立TargetActivity的實例。可是在實際的插件系統中,要啓動的目標Activity確定存在於一個單獨的文件中,系統默認的ClassLoader沒法加載插件中的Activity類——系統壓根兒就不知道要加載的插件在哪,談何加載?所以還有一個很重要的問題須要處理: