集成一個第三方相冊功能,只需集成一個插件APK到項目中,無需集成額外代碼,而且支持隨時更新相冊功能,無需發佈版本更新,無需AndroidManifest中聲明四大組件,這就是插件化。android
插件化可利用性很廣,但事實上大多數開發者,由於未知而放棄使用,因此本篇將深刻淺出帶你瞭解插件化原理,從基礎到實現,插件化再也不是你陌生的領域。git
本篇主要涉及到:github
ps:若是你對此(1、二)已經十分了解,請自行略過。編輯器
Activity和Service的啓動流程十分複雜,一個startActivity
的背後是無數的邏輯實現,這裏不深刻討論,但須要理解這個流程,由於插件化是在流程上動手腳,以達到繞過系統限制的目的。ide
下方圖片是Activity啓動的簡化流程,能夠看到,從Instrumentation
開始,到ActivityManagerService
和ActivityThread
結束,啓動一個Activity,流程並不簡單。工具
在Instrumentation
在execStartActivity
開始啓動,到經過checkStartActivityResult
校驗Activity是否在Manifest中聲明,從圖中能夠看出,流程仍是至關繁瑣的。
(Activity啓動流程詳見圖片)性能
下方圖片是Service啓動的簡化流程,一樣能夠看到,ActivityManagerService
和ActivityThread
一樣起到了關鍵性的做用。插件化的關鍵,就在於Instrumentation
、ActivityManagerService
和ActivityThread
。優化
(Service啓動流程詳見圖片)插件
爲了更好理解插件化,如下圖,是幾個關鍵類的對應關係與實際做用,有點S/C的味道。它們的通訊是經過IBinder
,實現進程通訊的,能夠看出,啓動Activity和Service,ActivityThread
和ActivityManagerService
是關鍵,而且上面咱們知道,Instrumentation
是Activity的啓動入口,因此實現插件化的流程,即可以在這些關鍵類上開刀。3d
(下圖在插件化實現中起到關鍵做用)
好了,帶了一波基礎姿式的節奏,稍安勿躁,先這裏在補充幾個概念,若是你已經習得,能夠跳過:
Hook:攔截某個內部流程,在其中作某些修改,以實現本身的邏輯。
Instrumentation:每一個Activity都有一個Instrumentation
對象,它是在Activity啓動是被賦予的Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity();
這即是startActivityForResult的啓動,同時返回啓動結果。
佔坑:聲明一個不存在的Activity,如:<activity android:name=".A$1" android:launchMode="standard"/>
,這樣啓動.A$1這個Activity能夠欺騙系統檢測,而後再將插件Activity注入到.A$1這個坑位中。
插件化的實現就是在於加載、繞過系統限制、啓動和管理插件等過程。按照VirtualApk的實現,大體流程爲:
一、初始化Hook住Instrumentation
和ActivityThread
等。經過PackageParser(插件apk包信息)、AssetManager(資源文件Resources)、ClassLoader等加載一個Apk插件。
二、啓動插件Activity:提早在主APP中佔有坑位,經過替換Intent中的targetActivity,打開佔坑聲明的A$Activity,而後繞過AndroidManifest檢測,再攔截newActivity方法中恢復targetActivity。
三、啓動插件Service:經過啓動一個代理Service統一管理,攔截全部Service方法,修改成startService到代理Service,在代理Service的onStartCommond
統一管理,建立/中止目標service 。
初始化過程當中,VirtualApk 建立了PluginManager ,而且hook住了Instrumentation
和SystemService,以下圖所示。
如下圖所示,VrutalApk經過Instrumentation
建立了一個VAInstrumentation
對象,VAInstrumentation
是一個繼承Instrumentation
的類。
將VAInstrumentation
反射插入到ActivityThread
中,這樣系統接下來關於Instrumentation
的操做,就會回到VAInstrumentation
中,被VrtualApk接管。
這裏是如何拿到Instrumentation
的?
由於在ActivityThread
內部有一個sCurrentActivityThread
靜態變量。以下圖,經過反射sCurrentActivityThread
咱們能夠獲取當前ActivityThread
,而ActivityThread
的公開方法getInstrumentation
便可拿到Instrumentation對象。
另外,上方圖1還有設置HandlerCallback
的流程,其實就是攔截了ActivityThread
中的mH
這個Handler的Callback,從【 1、 Activity/Service啓動流程】流程圖能夠看到,mH的handleMessage處理不少Activity的啓動狀態。
以下圖, 是Hook Service的流程,如圖中註釋所示,經過ActivityManagerNative
的getDefault
,拿到AndroidManagerService
(詳見啓動流程圖),而VirtualApk經過自定義ActivityManagerProxy
,從新生成了一個IActivityManager
,而後注入回AndroidManagerService
中,這樣接管了系統啓動、管理service等操做。
加載插件APK是經過PluginManager
的loadPlugin
方法,以下圖所示,此處就是將apk拆開,解析,讀取,加載,組裝爲LoadedPlugin並保存,以方便後面管理與使用。
此處對Apk進行了複雜的解析、加載、合併等操做,大體流程以下:
AssetManager
建立Resource
對象,平臺用AssetManager建立出Resource,判斷是否和宿主Apk合併資源。 那麼是時候啓動插件Activity了。經過startActivity即可以啓動。從上面的流程咱們知道啓動是從Instrumentation.execStartActivity();
開始的,而系統的Instrumentation
已經被VAInstrumentation
替換,其中VAInstrumentation
重寫了幾個關鍵方法:
沒錯,以下圖,在啓動Activity的入口處,VirtualApk攔截了請求,而後根據Intent的參數,去匹配plugin中的Activity坑位,以後替換Intent中的Activity,以此來達到欺騙系統的效果。
可是,由於這個Activity的對象了實際上並不存在,最終咱們須要啓動的是實現了的targetActivity,因此須要攔截Instrumentation
的第二個方法newActivity
,由於在ActivityTread
的performLaunchActivity
中,會調用Instrumentation
的 newActivity
。
在newActivity
中,以下圖,類沒有找到時(坑位類確定找不到啦),那麼就去獲取本來保存在Intent的目標Activity,而後調用建立VAInstrumentation
時保存的Instrumentation
(mBase)去建立Activity。
Activity雖然建立好了,可是它對應的資源和context都還不對,因此咱們須要在Activity的OnCreate以前完成好Resource的注入。前面加載Apk時,這些資源都保存在Plugin
中。因此咱們攔截callActivityOnCreate
方法,以下圖,將Activity的Context、Application、Reource,都替換成Plugin中對應的對象。一個Activity就這樣繞過AndroidManifest啓動起來了。
startService啓動Service時,還記得上面咱們經過ActivityManagerProxy
生成IActivityManager
嗎?它主要攔截了Service相關啓動和中止等方法,而後將其都轉化爲對應的startService方法,指向代理Service。由於startService方法的特性,他們最終都會在代理Service的onStartCommand
中被統一處理。
以下圖,是ActivityManagerProxy
,其中invoke攔截了全部相關的服務請求,並作了轉化處理,下面以startService爲例。
startService這裏,主要即是提取本來目標service信息,而後轉化爲代理Service,發送到代理Service,下方圖片爲啓動流程和轉化流程。
以下圖,在代理service中,根據請求類型,代理service會經過classLoader加載來建立service,並操做其attach、onCreate、onStartCommand等,讓service工做起來。
自此Activity和Service都成功啓動了,是否是對插件化有了不同的瞭解?
允許這裏插入這一塊,安利下Virtual中的AndroidStub模塊,以下圖
由於都用反射很浪費性能,因此有了AndroidStub
,它是用來欺騙編譯器的。正常狀況下你想操做ActivityThread
就會出現以下圖狀況,由於它是一個@hide
類,這時候除了反射獲得ActivityThread
,你還須要再反射須要執着方法才能執行,這在必定程度會損耗一些性能。
可是以下圖,VirtualApk經過AndroidStub
,模擬源碼建立了如ActivityThread
類,這裏你就能夠如圖正常使用ActivityThread
了,而AndroidStub
中的ActivityThread
,其實只是定義了和原碼中一摸同樣的方法,並無其餘實現。
由於CoreLibrary
依賴AndroidStub
使用的是provided
,由於provided
依賴是不打包依賴包,而是運行時提供,因此成功欺騙了編輯器,以此提升了性能。很神奇吧?
終於結束了,若是你看到了這裏,相信你是一個頗有耐心的同志!固然插件化仍是其餘實現方式,如Replugin,只Hook住了ClassLoader,流程更加複雜,若有什麼建議和疑問,歡迎留言討論。
VirtualApk:github.com/didi/Virtua…
我的github:github.com/CarGuo