你們好,我係蒼王。
架構
如下是我這個系列的相關文章,有興趣能夠參考一下,能夠給個喜歡或者關注個人文章。app
[Android]如何作一個崩潰率少於千分之三噶應用app--章節列表ide
什麼是佔坑?爲何要佔坑?函數
Android插件化中,從一個插件Activity跳轉到不一樣插件的Activity的時候,是否能夠能正常跳轉成功?組件化
聲明Activity須要配置什麼?學習
聲明Activity是須要AndroidManifest中聲明,可是插件是依賴於宿主的,插件聲明瞭Activity,可是插件的AndroidManifest信息,是沒法動態配置到宿主裏面的。gradle
那AndroidManifest在何時注入的?其實這個是在App安裝的時候,PackageManagerService就會讀取到AndroidManifest裏面的配置信息並保存一份到PackageManagerService.Settings當中,那麼基本沒法動態的改變這份配置信息。若是之後能動態的改變Android中記錄的App配置信息,那麼咱們就不須要佔坑了。ui
正由於一開始就已經將配置AndroidManifest記錄到PackageManagerService,裏面的記錄的Activity的信息,將也會保存到PackageManagerService中。咱們使用startActivity的時候,ActivityManagerService將會Activity的合法信息傳送到Native層做配置驗證,若是沒法找到跳轉Activity的配置,那麼將拋出異常。插件
插件是app運行時,動態將插件信息插入classLoader的dex列表當中。可是宿主的AndroidManifest配置是沒法動態去配置修改的。那麼插件中的跳轉,如何越過這種困境呢?3d
工程師聰明的,他們提早在宿主聲明一些空Activity信息到AndoridManifest當中,而後在使用startActivity後在ActivityManagerService中在跳轉到Native層前將替換成員AndroidManifest的空Activity,欺騙驗證,而後Native層驗證事後,在傳回ActivityManagerService層後替換回須要跳轉的Activity的信息。這種聲明空Activity信息到AndroidManifest的行爲,咱們就叫作佔坑了。
結合上一節,hook點來看,佔坑替換是須要hook掉ActivityManagerService來完成這樣的操做的,可是上一節已經介紹過Replugin惟一hook點在classloader了,那麼這個佔坑替換又是如何完成呢?
宿主在引入gradle-host-library的時候,就已經引入了Replugin的佔坑操做了。
Replugin在庫中的AndroidManifest,已經提早的聲明瞭各類各樣的Activity Service Proivder,而後BroadcastReceiver能夠動態註冊,因此並不須要佔坑。
咱們能夠看到${applicationId}它將會直接引用到宿主app build.gradle中的applicationId完成。
咱們能夠看到這些坑位會被合併到在宿主的full的AndroidManifest.xml裏面。
你拖到最後,會發現除了這些坑位外,還會有不少360的坑位添加了,這是如何作到的呢?
這裏關鍵在於引用了replugin-host-gradle中的配置,咱們在ComponentsGenerator.groovy文件,會使用Gradle命令編譯時生成這些佔坑聲明。以後深刻介紹replugin的gradle文件的時候,會給你們更加深刻介紹。
咱們看一下使用Replugin封裝的的跳轉
很清晰的看到pluginName,至關於Android的包名來填寫。
startActivity的時候,從intent中獲取包名和類名,而後再調用Factory.startActivityWithNoInjectCN
而後繼續使用插件管理的繼續跳轉函數
這裏IPluginManager對參數等說明很是狀況,說明是公司的技術追求仍是很高的。
進入到底層的PmLocalImpl中
而後更深刻添加參數
在PmInternalImpl終於能夠看真正實現,這裏先要判斷是否插件已經下載,getPluginConfigInfo會獲取是否存在手機中是否存在插件。
而後isNeedToDownLoad來啓動下載。
這裏就是一些基礎的下載封裝了。
下載時須要上鎖處理,表示當前插件正在生效。ProcessLocker是自定義的進程鎖。
PluginProcressMain.getPluginHost().pluginDownloaded將會下載並加載插件,咱們留到下一節再介紹,這個過程。
tryLock和unlock的調用就是對進程的鎖定了。
ProcessLock裏面,是使用文件鎖來完成上鎖的,這裏的進程鎖,正確的來講是文件鎖。
這裏面建立出文件後綴爲.lock的文件,做爲文件鎖,而後建立出FileOutputStream爲輸出通道,
Java NIO中的FileChannel是一個鏈接到文件的通道。能夠經過文件通道讀寫文件。
FileChannel沒法設置爲非阻塞模式,它老是運行在阻塞模式下。
這裏的FileChannel是FileOutputStream中獲取的通道的。
這裏面須要釋放的時候,須要釋放Filelock,FileChannel, FileOutStream, File,四個對象造成的鎖。
當正常安裝之後,了經過獲取到PluginInInfo來判斷插件是否成功安裝
而後再次下載中會經過onPluginNotExitsForActivtiy,來回調提示。
若是activity是動態註冊的類,直接使用startActivity打開
這裏面須要判斷插件中有註冊到註冊的Activity類
這裏是經過HashMap來保存類的列表
這裏其餘插件首先會在Entry的入口裏面在init的時候調用註冊的方法註冊,建立出一個ProxyRePluginVar的遠程插件信息。
其會建立出兩個startActivity的MethodInvoker反射的類,來用於使用跳轉方法。其會分發到不一樣的插件的RePlugin的對象
Broadcast,Provider,Service,四大組件都是經過這種反射調用的方式,來提供其餘插件調用的。
回到PmInternalImpl,插件損壞或者其餘緣由狀態異常,判讀會返回跳轉目標不存在
若是是大插件,會使用onLoadLargePluginForActivity的方法啓動。
這裏真正的啓動佔坑的方式來作跳轉
咱們看到loadPluginActivity當中,經過ActivityInfo 來保存一個Activity的信息,而後
這裏判斷進程和遠程分配坑位。
若是有分配,馬上進入監控狀態,並強制使用UI進程運行。
使用bindActivity來綁定Activity.
bindActivity當中,繼續調用到PluginContainter的alloc分配
最終會調用到allocLocked分配,裏面有四種規則
(1)嘗試找找到一個動態註冊過的。
(2)找一個新分配的
(3)重用,最老的一個
(4)擠掉最老的一個
而後經過坑位跳轉
咱們在plugin-lib中的插件須要依賴的庫中找到
其PluginActivity是替換的Activity
可是實際上demo中並無使用繼承PluginActivity的例子。都是使用佔坑邏輯來替換,並不必定要使用PluginActivity。使用PluginActivity是嵌套生命週期的方法給Repluin管理。
Replugin佔坑跳轉的判斷是我研究插件化以來最複雜的,代碼量也很大。
我創建了一個關於Android架構學習的羣,裏面能夠進一步進行組件化學習的交流。
羣號是316556016,也能夠掃碼進羣。我在這裏期待大家的加入!!!