在上一篇文章Android插件化之ClassLoader中,咱們已經能夠成功加載apk,可是尚未辦法啓動插件中的Activity。咱們知道,若是要啓動一個Activity,那麼這個Activity必須在AndroidManifest.xml中註冊。所以,若是咱們要啓動插件中的Activity,那麼這個Activity事先必須在宿主的AndroidManifest.xml中預註冊。這樣一來就會有兩個問題:git
所以,現有的插件化框架都會有一套越過AndroidManifest.xml註冊而啓動Activity的機制。本文的目的就是分析這套機制。github
不考慮多進程的狀況,Activity的啓動是Binder雙向通訊的一個過程,它由咱們的app進程和AMS所在的system_server進程共同完成。system_server是一個系統級的進程,其內部的AMS負責管理全部app的Activity狀態。所以,Android是不容許咱們對AMS進行修改的。那麼,插件化技術只能在咱們app進程裏作文章了。api
下圖我根據6.0的源碼畫出了Activity的啓動流程圖,我省略了咱們不須要關心的AMS部分。 app
正如上圖所示,我將Activity的啓動流程分爲了兩部分。上部分描述了app從發起啓動activity請求到AMS接受到該請求的過程。下部分描述了AMS處理完成後響應app進程啓動Activity的過程。框架
上圖不管是Activity的statActivity()仍是startActivityForResult()最終都調用了Instrumentation.execStartActivity()。咱們繼續定位到Instrumentation.execStartActivity()。post
execStartActivity()很是簡單。它將傳進來的參數進一步包裝後傳給ActivityManagerNative.getDefault()的startActivity()。ActivityManagerNative.getDefault()返回的實際上是一個ActivityManagerProxy對象。ActivityManagerProxy是ActivityManagerService在app進程中的Binder代理對象。調用ActivityManagerProxy.startService()最後會調用ActivityManagerService.startService()。這樣請求就到了ActivityManagerService。ActivityManagerNative.getDefault()以下:學習
到此,請求啓動Activity的過程就分析完了。插件
前面咱們提到過,在不考慮多進程的狀況下,Activity的啓動過程是一個Binder雙向通訊的過程。AMS要主動與app進程通訊要依靠請求啓動Activity階段傳過來的IBinder對象,這個IBinder對象就是上面介紹過的Instrumentation.execStartActivity()中的 whoThread對象,它其實是一個ApplicationThreadProxy對象,用來和ApplicationThread通訊。AMS通知app進程啓動Activity是經過調用ApplicationThreadProxy.scheduleLaunchActivity()完成的。根據Binder通訊,ApplicationThread.scheduleLaunchActivity()會被調用。咱們就從ApplicationThread.scheduleLaunchActivity()開始分析。3d
scheduleLaunchActivity()將從AMS中傳過來的參數封裝成ActivityClientRecord對象,而後將消息發送給mH,mH是一個Handler對象。 代理
H是ActivityThread的內部類,繼承自Handler,它在收到LAUNCH_ACTIVITY的消息後,會調用ActivityThread.handlerLaunchActivity()。
handleLaunchActivity()主要調用了兩個方法:performLaunchActivity()和handleResumeActivity()。performLaunchActivity()會完成Activity的建立,以及調用Activity的onCreate()、onStart()等方法。handleResumeActivity()會完成Activity.onResume()的調用。咱們繼續跟蹤performLaunchActivity()。
上述代碼在關鍵位置都加了註釋。經過註釋咱們明白了Activity的建立以及onCreate()的調用都是在Instrumentation中完成的。這裏須要注意一下Activtiy.attach()的調用時機,咱們會在下文中用到。Instrumentation具體的代碼以下:
到此AMS通知app進程啓動Activity的流程就結束了。
以上內容算是內功部分,接下來就到了學習具體招式的時候了。
要啓動沒有在AndroidManifest.xml中註冊的Activity,其核心是就是偷天換日。怎麼作呢?經過一個例子說明。
假如在插件中有一個未在AndroidManifest.xml註冊的TargetActivity,咱們想啓動它,能夠分爲三步。
第一步十分簡單,沒什麼好說的。要實現第二步和第三步就須要用到Activity啓動流程的知識了。
在Activity啓動流程中,Instrumentation不管在請求階段仍是響應階段都扮演着重要的角色。在請求階段Instrumentation.execStartActivity()會被調用,而在響應階段Instrumentation.newActivity()會被調用。所以若是咱們能夠Hook Instrumentation,那麼咱們就能夠在execStartActivity()和newActivity()分別完成第二步和第三步中的功能。
咱們知道,每個Java程序都有一個main()方法。Android App的main()方法就在ActivityThread中。
在main()方法中,ActivityThread會被初始化並最終把對象保存在靜態的sCurrentActivityThread中。在一個app進程中只有一個ActivityThread實例sCurrentActivityThread。sCurrentActivityThread能夠經過ActivityThread.currentActivityThread()拿到。
attach()中,mgr.attachApplication(mAppThread)這段代碼又是一個Binder雙向通訊的過程,它主要爲建立Application對象服務。整個通訊過程和Activity啓動過程相似,我就再也不詳細介紹了。在通訊的最後,ActivtiyThread.handleBindApplication()被調用,而在方法內部,Instrumentation被初始化。
總結一下,一個App進程,只有一個ActivityThread對象,這個對象保存在sCurrentActivityThread中,能夠經過ActivityThread.currentActivityThread()獲取。ActivityThread的mInstrumentation會在Application建立以前初始化。
Activtiy中的Instrumentation是經過Activity.attach()傳進來的。
Activity.attach()在介紹Activity啓動流程時提到過。它會在ActivityThread.performLaunchActivity()中被調用。
這樣ActivtyThread把本身內部的Instrumentation傳遞到了Activity中。
經過以上分析,咱們知道,要Hook app的Instrumentation,只須要替換掉ActivityThread的Instrumentation便可。可是,Android SDK沒有爲咱們提供任何關於ActivityThread的api。要訪問Android SDK中不存在的類或方法,咱們學習一下VirtualAPK是怎麼作的。
在VirtualAPK中有個叫AndroidStub的module。它的結構以下:
VirtualAPK又從新聲明瞭這些Android SDK沒有提供的Framework層的類。這些類只有方法的聲明,如ActivityThread中的內容以下:
這樣咱們就可使用這些Android SDK沒有提供的類或隱藏的方法了。須要注意的一點是,AndroidStub應該只參與編譯過程,這很簡單,用compileOnly依賴就能夠了。
接下來,咱們就能夠經過反射替換ActivitThread的Instrumentation了。代碼以下:
上面的VAInstrumentation是對系統Instrumentation的代理類。在VAInstrumentation的內部咱們能夠加入任何咱們想要的邏輯。
在Instrumentation.execStartActivity()執行前將咱們要啓動的Activity替換成預註冊的ProxyActivity。
在Instrumentation.newActivity()執行前將預註冊的ProxyActivity替換回咱們要啓動的Activity。
在Android插件化開篇中我說過,每一篇文章的最後都會是一個Demo,這些Demo串聯起來就是一個插件化框架,因此我在Github上建了一個項目VirtualApkLike,Demo都會以不一樣的分支放到這裏。本文的Demo在startActtivity分支上。