[Android]用架构师角度看插件化(3)-Replugin 需要占坑跳转?
一.占坑
什么是占坑?為什么要占坑?
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可以動態注冊,所以并不需要占坑。
我們可以看到${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占坑跳轉的判斷是我研究插件化以來最復雜的,代碼量也很大。
作者:Cang_Wang
鏈接:https://juejin.im/post/597fe19751882556e0331c59
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的[Android]用架构师角度看插件化(3)-Replugin 需要占坑跳转?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Android]你不知道的Androi
- 下一篇: [Android]用架构师角度看插件化(