[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

Cang_Wang發表於2019-02-23

大家好,我係蒼王。

以下是我這個系列的相關文章,有興趣可以參考一下,可以給個喜歡或者關注我的文章。

[Android]如何做一個崩潰率少於千分之三噶應用app--章節列表


一.佔坑

什麼是佔坑?為什麼要佔坑?

Android外掛化中,從一個外掛Activity跳轉到不同外掛的Activity的時候,是否可以能正常跳轉成功?

宣告Activity需要配置什麼?

宣告Activity是需要AndroidManifest中宣告,但是外掛是依賴於宿主的,外掛宣告瞭Activity,但是外掛的AndroidManifest資訊,是無法動態配置到宿主裡面的。

那AndroidManifest在什麼時候注入的?其實這個是在App安裝的時候,PackageManagerService就會讀取到AndroidManifest裡面的配置資訊並儲存一份到PackageManagerService.Settings當中,那麼基本無法動態的改變這份配置資訊。如果以後能動態的改變Android中記錄的App配置資訊,那麼我們就不需要佔坑了。

正因為一開始就已經將配置AndroidManifest記錄到PackageManagerService,裡面的記錄的Activity的資訊,將也會儲存到PackageManagerService中。我們使用startActivity的時候,ActivityManagerService將會Activity的合法資訊傳送到Native層作配置驗證,如果無法找到跳轉Activity的配置,那麼將丟擲異常。

外掛是app執行時,動態將外掛資訊插入classLoader的dex列表當中。但是宿主的AndroidManifest配置是無法動態去配置修改的。那麼外掛中的跳轉,如何越過這種困境呢?

工程師聰明的,他們提前在宿主宣告一些空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可以動態註冊,所以並不需要佔坑。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

我們可以看到${applicationId}它將會直接引用到宿主app build.gradle中的applicationId完成。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

我們可以看到這些坑位會被合併到在宿主的full的AndroidManifest.xml裡面。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

你拖到最後,會發現除了這些坑位外,還會有很多360的坑位新增了,這是如何做到的呢?

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

這裡關鍵在於引用了replugin-host-gradle中的配置,我們在ComponentsGenerator.groovy檔案,會使用Gradle命令編譯時生成這些佔坑宣告。之後深入介紹replugin的gradle檔案的時候,會給大家更加深入介紹。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

二.Replugin 跳轉流程

我們看一下使用Replugin封裝的的跳轉

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

很清晰的看到pluginName,相當於Android的包名來填寫。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

startActivity的時候,從intent中獲取包名和類名,然後再呼叫Factory.startActivityWithNoInjectCN

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

然後繼續使用外掛管理的繼續跳轉函式

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

這裡IPluginManager對引數等說明非常情況,說明是公司的技術追求還是很高的。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

進入到底層的PmLocalImpl中

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

然後更深入新增引數

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

在PmInternalImpl終於可以看真正實現,這裡先要判斷是否外掛已經下載,getPluginConfigInfo會獲取是否存在手機中是否存在外掛。

然後isNeedToDownLoad來啟動下載。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

這裡就是一些基礎的下載封裝了。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

下載時需要上鎖處理,表示當前外掛正在生效。ProcessLocker是自定義的程式鎖。

PluginProcressMain.getPluginHost().pluginDownloaded將會下載並載入外掛,我們留到下一節再介紹,這個過程。

tryLock和unlock的呼叫就是對程式的鎖定了。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

ProcessLock裡面,是使用檔案鎖來完成上鎖的,這裡的程式鎖,正確的來說是檔案鎖。

這裡面建立出檔案字尾為.lock的檔案,作為檔案鎖,然後建立出FileOutputStream為輸出通道,

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?


Java NIO中的FileChannel是一個連線到檔案的通道。可以通過檔案通道讀寫檔案。

FileChannel無法設定為非阻塞模式,它總是執行在阻塞模式下。

這裡的FileChannel是FileOutputStream中獲取的通道的。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

這裡面需要釋放的時候,需要釋放Filelock,FileChannel, FileOutStream, File,四個物件形成的鎖。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

當正常安裝以後,了通過獲取到PluginInInfo來判斷外掛是否成功安裝

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

然後再次下載中會通過onPluginNotExitsForActivtiy,來回撥提示。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

如果activity是動態註冊的類,直接使用startActivity開啟

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

這裡面需要判斷外掛中有註冊到註冊的Activity類

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

這裡是通過HashMap來儲存類的列表

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

這裡其他外掛首先會在Entry的入口裡面在init的時候呼叫註冊的方法註冊,建立出一個ProxyRePluginVar的遠端外掛資訊。

其會建立出兩個startActivity的MethodInvoker反射的類,來用於使用跳轉方法。其會分發到不同的外掛的RePlugin的物件

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

Broadcast,Provider,Service,四大元件都是通過這種反射呼叫的方式,來提供其他外掛呼叫的。


回到PmInternalImpl,外掛損壞或者其他原因狀態異常,判讀會返回跳轉目標不存在

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?


如果是大外掛,會使用onLoadLargePluginForActivity的方法啟動。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

這裡真正的啟動佔坑的方式來做跳轉

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

我們看到loadPluginActivity當中,通過ActivityInfo 來儲存一個Activity的資訊,然後

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

這裡判斷程式和遠端分配坑位。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

如果有分配,立刻進入監控狀態,並強制使用UI程式執行。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

使用bindActivity來繫結Activity.

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

bindActivity當中,繼續呼叫到PluginContainter的alloc分配

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

最終會呼叫到allocLocked分配,裡面有四種規則

(1)嘗試找找到一個動態註冊過的。

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

(2)找一個新分配的

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

(3)重用,最老的一個

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

(4)擠掉最老的一個

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

然後通過坑位跳轉

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

我們在plugin-lib中的外掛需要依賴的庫中找到

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

其PluginActivity是替換的Activity

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?

但是實際上demo中並沒有使用繼承PluginActivity的例子。都是使用佔坑邏輯來替換,並不一定要使用PluginActivity。使用PluginActivity是巢狀生命週期的方法給Repluin管理。

Replugin佔坑跳轉的判斷是我研究外掛化以來最複雜的,程式碼量也很大。


我建立了一個關於Android架構學習的群,裡面可以進一步進行元件化學習的交流。

群號是316556016,也可以掃碼進群。我在這裡期待你們的加入!!!

[Android]用架構師角度看外掛化(3)-Replugin 需要佔坑跳轉?


相關文章