酷狗 Android App 插件化實施過程(轉(zhuǎn)載)

原文在此

什么是插件化框架

插件化框架可以在主程序不重新安裝的情況下,針對單個業(yè)務(wù)模塊進行加載達到模塊更新的目的,整個加載更新過程财破,對用戶來說也是無感知的。
正式因為這樣从诲,新需求比起傳統(tǒng)更新方式覆蓋率和覆蓋速度都會更高和更快左痢,對于大型開發(fā)團隊,各個業(yè)務(wù)模塊開發(fā)小組組也不需要再等所有組的需求開發(fā)完統(tǒng)一發(fā)布版本系洛,發(fā)版本可以單獨針對小組內(nèi)單個功能發(fā)布了俊性,有了這些優(yōu)點才使得這1年來插件化框架如此流行的重要原因。

目前網(wǎng)上流行的主流的插件化技術(shù)核心主要分兩類:

一類是360公司開源的DroidPlugin描扯,特點在宿主程序上打造一個純粹的環(huán)境定页,可以讓一個個普通apk(插件)不安裝就可以正常使用,并且插件之前資源和代碼都是相互獨立互相不干擾也不能訪問绽诚,為了達到這樣的目的典徊,框架hook了大量的系統(tǒng)api。
另一類是從dynamic-load-apk 開始恩够,通過反射少量api卒落,達到插件代碼和資源與宿主合并,達到相互調(diào)用的結(jié)果蜂桶,目前大部分都是框架從底層代碼合并和資源合并用的手法都差不多儡毕,只是各個框架在這基礎(chǔ)上對插件化的理解不一樣,為各自的項目做了不少的業(yè)務(wù)封裝扑媚。

常用的類加載方法是:

常見的資源加載方式是:

目前看來怎么加載代碼腰湾,怎么加載資源雷恃、怎么動態(tài)聲明和啟動android組件。網(wǎng)上大部分開源的開源框架都很好的解決了這些怎么實現(xiàn)的問題檐盟,很完美的做到了從0到1褂萧。但是對于一個有幾十個開發(fā)人員千萬級別的大型的app來說,單解決了這些問題還是不夠的葵萎。插件框架的穩(wěn)定性导犹、從原有代碼到插件化的遷移成本、后期維護成本等等方面都需要考慮到羡忘。
所以兼容性谎痢、遷移成本、后期維護成本是我們在插件化選型時最基本的考慮因素卷雕。
首先兼容性节猿、后期維護成本,大家都了解到由于android系統(tǒng)的碎片化漫雕,android官方提供的api也存在著不少的兼容性問題滨嘱,況且針對創(chuàng)新能力如此強大的國內(nèi)手機廠商,國產(chǎn)手機也額外的多了不少兼容性問題浸间。
具體例子:常用到的資源加載方式太雨,放在vivo的部分手機就不能使用原因是其ROM把系統(tǒng)的Resources封裝成為了VivoResources直接導(dǎo)致了反射失敗插件資源無法加載,同樣的Nubia的部分手機也是魁蒜。所以基于這樣在做插件化的時候hook系統(tǒng)的api就應(yīng)該盡量的少囊扳,因為hook的api不確定性太多了,而且在這部分的開發(fā)過程肯定不會有任何文檔提供參考的兜看,遇到問題就干擼代碼吧锥咸。
處理兼容性的工作量越大其實后期的維護成本就越高,至少如果android一個新版本出來了首先要看的是之前hook的官方api有沒有被改掉细移。如果有問題還要再針對新的版本尋求新的實現(xiàn)搏予,這部分工作量是非常大的。這也是我們不選擇DroidPlugin的重要原因葫哗,從網(wǎng)上的能找到的所有資料并沒有看到DroidPlugin的兼容性能達到多少能適配多少臺手機缔刹。但是預(yù)判一下DroidPlugin hook了大量的api比起其他框架hook兩個,這部分后續(xù)維護成本也是足夠喝一壺的劣针。
遷移成本校镐,其實很多大型的項目實現(xiàn)插件化,在這個調(diào)整的過程中對代碼結(jié)構(gòu)捺典,調(diào)用邏輯等等的修改肯定是有的鸟廓。怎么保證這個改動是最少的,也是我們的考慮之一畢竟有改動就會產(chǎn)生bug,比較幸運的是,我們從打包腳本上下手在保證傳統(tǒng)的項目結(jié)構(gòu)和邏輯調(diào)用不改變的情況下實現(xiàn)模塊插件化引谜。讓插件化先跑起來牍陌,在實現(xiàn)之后再讓各個業(yè)務(wù)小組針對插件化的建議慢慢的完善和封裝插件和宿主之間的協(xié)議和約定。

插件化遷移過程:

首先员咽,看看我們酷狗原有的基礎(chǔ)項目結(jié)構(gòu):

項目底層是一個公用library 提供大部分的公共的基礎(chǔ)模塊毒涧,酷狗作為application作為主程序,其他聽看唱其他業(yè)務(wù)模塊也作為一個個library,各個業(yè)務(wù)組關(guān)聯(lián)公共模塊和酷狗主程序贝室,在各自的業(yè)務(wù)模塊下開發(fā)契讲、調(diào)試。當(dāng)發(fā)版本的時候就統(tǒng)一在打包平臺上讓酷狗關(guān)聯(lián)所有業(yè)務(wù)模塊 然后統(tǒng)一打包滑频,這是最常見的項目組成架構(gòu)業(yè)務(wù)模塊有項目級別的代碼分離而且業(yè)務(wù)項目依賴公共基礎(chǔ)庫捡偏。
項目優(yōu)化目標

優(yōu)化后,業(yè)務(wù)組之前的開發(fā)方式完全不變峡迷,項目結(jié)構(gòu)對比優(yōu)化前完整保留银伟,打包之后每個業(yè)務(wù)模塊是一個個插件可以單獨加載運行,每個插件都是只包含插件自己的資源和代碼(不包含公共庫)绘搞,插件可以正常訪問宿主的資源和代碼彤避,只要宿主保留了插件所需的資源和代碼,無論宿主怎么改變都可以啟動插件夯辖。
在這個過程中主要需要解決的問題有:
打包插件只保留插件本身的代碼忠藤,打包后插件不改變?nèi)魏握{(diào)用邏輯能順利調(diào)用回宿主邏輯。
決插件和宿主資源沖突問題楼雹,插件只保留本身資源,插件能訪問到宿主資源尖阔。
重新編譯之后怎么保證舊的宿主能支持新的插件贮缅。

首先怎么把原來跟底層項目依賴的業(yè)務(wù)模塊 單獨打成一個插件包 只保留業(yè)務(wù)模塊的代碼



我們拿聽模塊做個例子先編譯宿主程序也就是酷狗項目和底層基礎(chǔ)庫,一直編譯完javac這時候主項目資源R.java和映射表都可以得到介却,然后把編譯出來的class打包成common.jar把common項目資源復(fù)制到一個空殼項目commonres谴供。
接著修改聽項目的屬性把它從一個library變成一個application,關(guān)聯(lián)讓它不直接關(guān)聯(lián)基礎(chǔ)庫齿坷,而是讓它關(guān)聯(lián) commonn.jar 和 commonres桂肌,其中common.jar做提供編譯。
按照這樣編譯下去永淌,聽項目編譯出來的apk就只包含自己的代碼了崎场。
接下來解決資源問題,正常的資源查找方式

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

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

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

最后酱讶,刪除插件resources.arsc多余的資源id和插件多余的資源文件退盯。這樣下來 最終得出的插件包 就是只含有插件代碼和插件資源的 而且還能隨意訪問宿主資源和代碼。
最后我們看看整體的編譯流程泻肯。

與微信資源混淆工具的兼容性問題
插件化工具主要在編譯時修改ID和去除其他多余資源渊迁,資源混淆工具主要是把名稱和路徑改短不修改ID,所以并不沖突灶挟。
只要保證讀寫操作都是嚴格按照 resources.arsc 的格式去寫就可以了琉朽。
接下來最后一個問題,重新編譯之后怎么保證舊的宿主能支持新的插件,簡單說就是多程序怎么一起 做代碼混淆,怎么保持宿主的資源ID。

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

我們選擇的是統(tǒng)一做混淆稚铣,為什么不能先混淆整體混淆一個項目然后再混淆第二個項目的時候保持用上個項目的mapping 繼續(xù)混淆,一直這樣編譯下去箱叁?
主要因為插件和宿主公共庫之間并沒有固定接口,單獨混淆原來直接關(guān)聯(lián)調(diào)用的方法就會被混淆移除掉惕医。
我們還記得插件模塊和common基礎(chǔ)模塊本來就是直接關(guān)聯(lián)耕漱、直接調(diào)用的,后面我們改變項目結(jié)構(gòu)讓插件獨立出來了抬伺,但是這部分調(diào)用還是存在的螟够。
一旦單獨混淆他們之間關(guān)聯(lián)的代碼就會被移除掉,宿主公共庫的final靜態(tài)變量混淆后也會消失峡钓,插件也沒法調(diào)用得到妓笙。
其實正常來說,宿主和插件之間的調(diào)用本來就是需要先有固定的接口做好解耦 規(guī)范好所有的調(diào)用能岩,宿主提供一套完整的api給插件使用寞宫,然后混淆的時候 keep好各自邊界 。 這樣對于后續(xù)插件版本更新和管理才是最正確的拉鹃。
為什么這個問題到現(xiàn)在才聊呢淆九,因為讓各個業(yè)務(wù)模塊組為了插件化然后去封裝接口统锤,等他們解耦封裝好才來做的話時間太長了,所以我們先用這種方式讓他們不需要做任何封裝和解耦就能用炭庙,后續(xù)再要求他們慢慢的規(guī)范好這部分的接口饲窿。

** 怎么keep資源問題**

我們知道資源id的生成是按照資源名稱隨機生成的,一旦添加或者修改了某個資源名稱所有的資源id都有可能改變焕蹄。 如果不能固定資源id 每次編譯都id都變的話插件也無法下發(fā)給用戶使用逾雄。
解決方案是在編譯的時候根據(jù)宿主R.java的生成ids.xml和public.xml下次編譯把ids.xml和public.xml放到宿主的/res/value目錄下編譯可保持id不變,這樣即使下次宿主的其他資源改變了腻脏,只要插件用到的所有資源沒有改變鸦泳,新打出來的插件 一樣是可以給舊的宿主使用的。
到這里一個完成的插件包已經(jīng)出來了永品,剩下的就是 按照基本的加載方式做鹰,把這個插件加載進去就順利完成了。

最后在宿主實現(xiàn)插件管理功能鼎姐,這部分純粹就是基本的業(yè)務(wù)邏輯了钾麸。

  1. 下載校驗插件差異包。 (我們生成新的插件包上次到服務(wù)器炕桨,服務(wù)器就會與原始插件做差異對比饭尝,然后生成文件級別的差異文件,下發(fā)給用戶)
  2. 合并差異包對比插件版本號献宫。
  3. 加載前黑名單和白名單檢驗钥平。(某些插件版本必須強制加載,某些強制不能加載)
  4. 啟動時加載插件資源映射表姊途。(保證一啟動就可以查詢到所以資源涉瘾,而且這個反射效率很高,不耗時捷兰,也不耗內(nèi)存速度也很快)睡汹。
  5. 插件代碼選擇合適時機懶加載。 (因為加載dex的時候寂殉,需要耗時,5.0以下做opt,5.0以上做oat,而且時間還不短原在,所以需要挑合適的時機做懶加載友扰。)

最后,本文主要是我們在插件化過程中遇到一些問題的解決方案庶柿,其實每個解決方案都會有各自的取舍村怪,也無誰優(yōu)誰劣,如有更好的方案歡迎下面留言交流浮庐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甚负,一起剝皮案震驚了整個濱河市柬焕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梭域,老刑警劉巖斑举,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異病涨,居然都是意外死亡富玷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門既穆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赎懦,“玉大人,你說我怎么就攤上這事幻工±剑” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵囊颅,是天一觀的道長当悔。 經(jīng)常有香客問我,道長迁酸,這世上最難降的妖魔是什么先鱼? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮奸鬓,結(jié)果婚禮上焙畔,老公的妹妹穿的比我還像新娘。我一直安慰自己串远,他們只是感情好宏多,可當(dāng)我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著澡罚,像睡著了一般伸但。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上留搔,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天更胖,我揣著相機與錄音,去河邊找鬼隔显。 笑死却妨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的括眠。 我是一名探鬼主播彪标,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼掷豺!你這毒婦竟也來了捞烟?” 一聲冷哼從身側(cè)響起薄声,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎题画,沒想到半個月后默辨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡婴程,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年廓奕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片档叔。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡桌粉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衙四,到底是詐尸還是另有隱情铃肯,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布传蹈,位于F島的核電站押逼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏惦界。R本人自食惡果不足惜挑格,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沾歪。 院中可真熱鬧漂彤,春花似錦、人聲如沸灾搏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狂窑。三九已至媳板,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間泉哈,已是汗流浹背蛉幸。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留丛晦,地道東北人奕纫。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像采呐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子搁骑,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 什么是插件化框架 插件化框架可以在主程序不重新安裝的情況下斧吐,針對單個業(yè)務(wù)模塊進行加載達到模塊更新的目的又固,整個加載更...
    siganid閱讀 929評論 0 5
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,152評論 25 707
  • 最近幾年移動開發(fā)業(yè)界興起了「 插件化技術(shù) 」的旋風(fēng),各個大廠都推出了自己的插件化框架煤率,各種開源框架都評價自身功能優(yōu)...
    斜杠時光閱讀 3,947評論 1 36
  • 1.框架的選擇:本次選擇的插件化框架在經(jīng)過數(shù)次比較之后選擇了國產(chǎn)大神開發(fā)的目前在市場上較為流行的small插件化框...
    鳳天凌閱讀 3,528評論 8 11
  • 一個二十歲的男的仰冠,還像小孩子一樣嬉笑怒罵,可能是很沒用的蝶糯。
    浩蕩縹緲閱讀 179評論 0 0