[Android]用架構師角度看插件化(3)-Replugin 須要佔坑跳轉?

你們好,我係蒼王。
架構

如下是我這個系列的相關文章,有興趣能夠參考一下,能夠給個喜歡或者關注個人文章。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了,那麼這個佔坑替換又是如何完成呢?


二.Replugin 佔坑處理

宿主在引入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 跳轉流程

咱們看一下使用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,也能夠掃碼進羣。我在這裏期待大家的加入!!!

相關文章
相關標籤/搜索