酷狗 Android App 插件化實施過程

么是插件化框架

????插件化框架可以在主程序不重新安裝的情況下,針對單個業務??榻屑釉卮锏僥?楦碌哪康?,整個加載更新過程,對用戶來說也是無感知的。

????正式因為這樣,新需求比起傳統更新方式覆蓋率和覆蓋速度都會更高和更快,對于大型開發團隊,各個業務??榭⑿∽樽橐膊恍枰俚人兇櫚男棖罌⑼暉騁環⒉及姹?,發版本可以單獨針對小組內單個功能發布了,有了這些優點才使得這1年來插件化框架如此流行的重要原因。

目前網上流行的主流的插件化技術核心主要分兩類:

????一類是360公司開源的DroidPlugin,特點在宿主程序上打造一個純粹的環境,可以讓一個個普通apk(插件)不安裝就可以正常使用,并且插件之前資源和代碼都是相互獨立互相不干擾也不能訪問,為了達到這樣的目的,框架hook了大量的系統api。

????另一類是從dynamic-load-apk 開始,通過反射少量api,達到插件代碼和資源與宿主合并,達到相互調用的結果,目前大部分都是框架從底層代碼合并和資源合并用的手法都差不多,只是各個框架在這基礎上對插件化的理解不一樣,為各自的項目做了不少的業務封裝。

常用的類加載方法是:


常見的資源加載方式是:


????目前看來怎么加載代碼,怎么加載資源、怎么動態聲明和啟動android組件。網上大部分開源的開源框架都很好的解決了這些怎么實現的問題,很完美的做到了從0到1。但是對于一個有幾十個開發人員千萬級別的大型的app來說,單解決了這些問題還是不夠的。插件框架的穩定性、從原有代碼到插件化的遷移成本、后期維護成本等等方面都需要考慮到。
所以兼容性、遷移成本、后期維護成本是我們在插件化選型時最基本的考慮因素。

????首先兼容性、后期維護成本,大家都了解到由于android系統的碎片化,android官方提供的api也存在著不少的兼容性問題,況且針對創新能力如此強大的國內手機廠商,國產手機也額外的多了不少兼容性問題。

????具體例子:常用到的資源加載方式,放在vivo的部分手機就不能使用原因是其ROM把系統的Resources封裝成為了VivoResources直接導致了反射失敗插件資源無法加載,同樣的Nubia的部分手機也是。所以基于這樣在做插件化的時候hook系統的api就應該盡量的少,因為hook的api不確定性太多了,而且在這部分的開發過程肯定不會有任何文檔提供參考的,遇到問題就干擼代碼吧。

????處理兼容性的工作量越大其實后期的維護成本就越高,至少如果android一個新版本出來了首先要看的是之前hook的官方api有沒有被改掉。如果有問題還要再針對新的版本尋求新的實現,這部分工作量是非常大的。這也是我們不選擇DroidPlugin的重要原因,從網上的能找到的所有資料并沒有看到DroidPlugin的兼容性能達到多少能適配多少臺手機。但是預判一下DroidPlugin hook了大量的api比起其他框架hook兩個,這部分后續維護成本也是足夠喝一壺的。

????遷移成本,其實很多大型的項目實現插件化,在這個調整的過程中對代碼結構,調用邏輯等等的修改肯定是有的。怎么保證這個改動是最少的,也是我們的考慮之一畢竟有改動就會產生bug,比較幸運的是,我們從打包腳本上下手在保證傳統的項目結構和邏輯調用不改變的情況下實現??椴寮?。讓插件化先跑起來,在實現之后再讓各個業務小組針對插件化的建議慢慢的完善和封裝插件和宿主之間的協議和約定。

插件化遷移過程:

????首先,看看我們酷狗原有的基礎項目結構:


????項目底層是一個公用library 提供大部分的公共的基礎???,酷狗作為application作為主程序,其他聽看唱其他業務??橐滄魑桓齦鰈ibrary,各個業務組關聯公共??楹涂峁分鞒絳?,在各自的業務??橄驢?、調試。當發版本的時候就統一在打包平臺上讓酷狗關聯所有業務???然后統一打包,這是最常見的項目組成架構業務??橛邢钅考侗鸕拇敕擲攵乙滴襝钅懇覽倒不】?。

????項目優化目標


????優化后,業務組之前的開發方式完全不變,項目結構對比優化前完整保留,打包之后每個業務??槭且桓齦霾寮梢緣ザ蘭釉卦誦?,每個插件都是只包含插件自己的資源和代碼(不包含公共庫),插件可以正常訪問宿主的資源和代碼,只要宿主保留了插件所需的資源和代碼,無論宿主怎么改變都可以啟動插件。

在這個過程中主要需要解決的問題有:

  1. 打包插件只保留插件本身的代碼,打包后插件不改變任何調用邏輯能順利調用回宿主邏輯。

  2. 決插件和宿主資源沖突問題,插件只保留本身資源,插件能訪問到宿主資源。

  3. 重新編譯之后怎么保證舊的宿主能支持新的插件。

????首先怎么把原來跟底層項目依賴的業務???單獨打成一個插件包 只保留業務??櫚拇?/h5>


????我們拿聽??樽齦隼酉缺嘁胨拗鞒絳蛞簿褪強峁廢钅亢偷撞慊】?,一直編譯完javac這時候主項目資源R.java和映射表都可以得到,然后把編譯出來的class打包成common.jar把common項目資源復制到一個空殼項目commonres。
接著修改聽項目的屬性把它從一個library變成一個application,關聯讓它不直接關聯基礎庫,而是讓它關聯 commonn.jar 和 commonres,其中common.jar做提供編譯。
按照這樣編譯下去,聽項目編譯出來的apk就只包含自己的代碼了。

????接下來解決資源問題,正常的資源查找方式


????應用層獲取資源 是用資源id直接去獲取,Resources先根據我們的id去資源映射表去查找這個資源的名稱是,拿到資源名稱不對應文件的資源只需要執行從資源ID到資源名稱的轉換即可,而對應有文件的資源還需要根據資源名稱來打開對應的文件。經過反射 resources 里面包含了多個映射表的目錄,查找的時候會按照順序先查宿主再查各個插件的映射表。


????資源沖突問題因為上面項目結構調整之后,插件和宿主都是application編譯時候就會出資源id相同,插件做資源id查找的時候就會有可能查找到宿主的資源,所以只要修改了resources.arsc和代碼層用到的R.java的id就可以解決沖突了常見的修改方式是修改插件的id的pp段。

????程序編譯到這里,修改關聯后的插件項目還保留了一份commonres資源,跟宿主的程序上的是一摸一樣的,能不能修改資源id來解決呢,答案是肯定的,因為插件和宿主查找資源的邏輯是一樣的,只要插件代碼調用中相同的資源id即R.java里面的id,修改為宿主資源id,資源查找的時候就會順利的到宿主的resources.arsc去查找資源了。


最后,刪除插件resources.arsc多余的資源id和插件多余的資源文件。這樣下來 最終得出的插件包 就是只含有插件代碼和插件資源的 而且還能隨意訪問宿主資源和代碼。

最后我們看看整體的編譯流程。


????與微信資源混淆工具的兼容性問題

????插件化工具主要在編譯時修改ID和去除其他多余資源,資源混淆工具主要是把名稱和路徑改短不修改ID,所以并不沖突。
只要保證讀寫操作都是嚴格按照 resources.arsc 的格式去寫就可以了。

接下來最后一個問題,重新編譯之后怎么保證舊的宿主能支持新的插件,簡單說就是多程序怎么一起 做代碼混淆,怎么保持宿主的資源ID。

????多項目一起混淆:

????我們選擇的是統一做混淆,為什么不能先混淆整體混淆一個項目然后再混淆第二個項目的時候保持用上個項目的mapping 繼續混淆,一直這樣編譯下去?

????主要因為插件和宿主公共庫之間并沒有固定接口,單獨混淆原來直接關聯調用的方法就會被混淆移除掉。
我們還記得插件??楹蚦ommon基礎??楸糾淳褪侵苯庸亓?、直接調用的,后面我們改變項目結構讓插件獨立出來了,但是這部分調用還是存在的。
一旦單獨混淆他們之間關聯的代碼就會被移除掉,宿主公共庫的final靜態變量混淆后也會消失,插件也沒法調用得到。

????其實正常來說,宿主和插件之間的調用本來就是需要先有固定的接口做好解耦 規范好所有的調用,宿主提供一套完整的api給插件使用,然后混淆的時候 keep好各自邊界 。 這樣對于后續插件版本更新和管理才是最正確的。
為什么這個問題到現在才聊呢,因為讓各個業務??樽槲瞬寮緩筧シ庾敖涌?,等他們解耦封裝好才來做的話時間太長了,所以我們先用這種方式讓他們不需要做任何封裝和解耦就能用,后續再要求他們慢慢的規范好這部分的接口。

????怎么keep資源問題

????我們知道資源id的生成是按照資源名稱隨機生成的,一旦添加或者修改了某個資源名稱所有的資源id都有可能改變。 如果不能固定資源id 每次編譯都id都變的話插件也無法下發給用戶使用。

????解決方案是在編譯的時候根據宿主R.java的生成ids.xml和public.xml下次編譯把ids.xml和public.xml放到宿主的/res/value目錄下編譯可保持id不變,這樣即使下次宿主的其他資源改變了,只要插件用到的所有資源沒有改變,新打出來的插件 一樣是可以給舊的宿主使用的。

????到這里一個完成的插件包已經出來了,剩下的就是 按照基本的加載方式,把這個插件加載進去就順利完成了。

????最后在宿主實現插件管理功能,這部分純粹就是基本的業務邏輯了。
  1. 下載校驗插件差異包。 (我們生成新的插件包上次到服務器,服務器就會與原始插件做差異對比,然后生成文件級別的差異文件,下發給用戶)

  2. 合并差異包對比插件版本號。

  3. 加載前黑名單和白名單檢驗。(某些插件版本必須強制加載,某些強制不能加載)

  4. 啟動時加載插件資源映射表。(保證一啟動就可以查詢到所以資源,而且這個反射效率很高,不耗時,也不耗內存速度也很快)。

  5. 插件代碼選擇合適時機懶加載。 (因為加載dex的時候,需要耗時,5.0以下做opt,5.0以上做oat,而且時間還不短,所以需要挑合適的時機做懶加載。)

來源:DiyCode

上一篇: 50個Android Studio秘訣、技巧和資源

下一篇: 高效使用AndroidStudio常用快捷操作

分享到: 更多
棋牌代理加盟 排列三万能五码稳赚 聚宝盆pk10计划软件下载 蓝洞棋牌下载 北京pk赛车免费计划app 3d最新买法稳赚不赔 124不倒翁投注法例子 赛车北京pk10记录 北京pk10倍投骗局 资金盘不能提现是什么情况 重庆时时彩到底有多假 云南时时历史开奖号码百度票 广东省11选5投注计划软件 pk10怎么看走势图选号 彩票转让合同 牛牛当庄赢钱的概率