Android插件化技術(shù)廣泛應(yīng)用在移動(dòng)開(kāi)發(fā)中,通過(guò)插件化技術(shù),AndroidApp可以不通過(guò)發(fā)布新版本而修復(fù)線上的bug或者推出新功能,這對(duì)于日活千萬(wàn)的App而言是非常重要的,可極大程度提升App開(kāi)發(fā)效率和質(zhì)量。本書詳細(xì)介紹了插件化技術(shù)的原理、各種方案,及其在AndroidApp中的使用。在介紹插件化技術(shù)的同時(shí),還詳細(xì)介紹了Android底層知識(shí),能幫助AndroidApp開(kāi)發(fā)人員更好地掌握Android的開(kāi)發(fā)技術(shù)。本書共22章,先介紹Android插件化技術(shù)的歷史和現(xiàn)狀,系統(tǒng)介紹了Android的底層知識(shí)以及反射、代理等技術(shù);然后介紹插件化技術(shù)的基本知識(shí),包括各類插件化解決方案及其對(duì)比;之后分析了一些插件化框架,如that、Zeus等,深入分析了資源及其在插件化中的應(yīng)用,以及混淆、增量更新等周邊技術(shù)的插件化解決方案。
本書不僅詳細(xì)介紹Android插件化技術(shù)如何實(shí)現(xiàn),而且包含大量Android系統(tǒng)的底層知識(shí),有助于App開(kāi)發(fā)人員深入理解Android系統(tǒng),從而寫出更健壯的代碼。Android插件化技術(shù)不僅適用于快速修復(fù)bug,還可以快速上線新功能,從而在時(shí)間上和競(jìng)爭(zhēng)對(duì)手搶占用戶。本書詳細(xì)介紹了插件化技術(shù)的各種方案,及其實(shí)現(xiàn)代碼,并給出了應(yīng)用場(chǎng)景。
前 言 Preface這是一本什么書如果只把本書當(dāng)作純粹介紹Android插件化技術(shù)的書那就錯(cuò)了。本書在研究Android插件化之余,還詳細(xì)介紹了Android系統(tǒng)的底層知識(shí),包括Binder和AIDL的原理、四大組件的原理、App的安裝和啟動(dòng)流程、Context和ClassLoader的家族史。沒(méi)有羅列大量的Android系統(tǒng)中的源碼,而是以一張張UML圖把這些知識(shí)串起來(lái)。
本書詳細(xì)介紹了Android中的資源機(jī)制,包括aapt命令的原理、resource文件的組成以及public.xml的使用方式,順帶還提及了如何自定義一個(gè)Gradle插件化。
此外,本書還介紹了so的加載原理,尤其是動(dòng)態(tài)加載so的技術(shù),可以幫助App進(jìn)行瘦身;探討了HTML5降級(jí)技術(shù),可以實(shí)現(xiàn)任何一個(gè)原生頁(yè)面和HTML5頁(yè)面的互換;介紹了反射技術(shù),以及jOOR這個(gè)有趣的開(kāi)源框架;介紹了Android中的動(dòng)態(tài)代理技術(shù)Proxy.newProxyInstance方法。
如果讀者能堅(jiān)持把這本書從頭到尾讀完,那么不僅掌握了插件化技術(shù),而且也把上述所有這些知識(shí)點(diǎn)全都系統(tǒng)地學(xué)習(xí)了一遍。也許Android插件化會(huì)隨著Google的限制而有所變化甚至消亡,但我在本書中介紹的其他知識(shí),仍然是大有用武之處的。
如何面對(duì)Android P的限制寫作這本書的時(shí)候,Google推出了Android P preview的操作系統(tǒng),會(huì)限制對(duì)@hide api的反射調(diào)用。目前會(huì)通過(guò)log發(fā)出警告,用戶代碼仍然能夠獲取到正確的Method或Field,在后續(xù)版本中獲取到的Method或Field極有可能為空。
但是道高一尺,魔高一丈。Google對(duì)這次限制,很快就被技術(shù)極客們繞過(guò)去了,有兩種解決方法:
1)把通過(guò)反射調(diào)用的系統(tǒng)內(nèi)部類改為直接調(diào)用。具體操作辦法是,在Android項(xiàng)目中新建一個(gè)庫(kù),把要反射的類的方法和字段復(fù)制一份到這個(gè)庫(kù)中,App對(duì)這個(gè)庫(kù)的引用關(guān)系設(shè)置為provided。那么我們就可以在App中直接調(diào)用這個(gè)類和方法,同時(shí),在編譯的時(shí)候,又不會(huì)把這些類包含到apk中。
其實(shí)早在2015年,hoxkx就在他的插件化框架中實(shí)現(xiàn)了這種技術(shù)。但是這種解決方案,僅限于Android系統(tǒng)中標(biāo)記為public的方法和字段,對(duì)于protected和private就無(wú)能為力了。比如AssetsManager的addAssetPath方法,ActivityThread的currentActivityThread方法。
2)類的每個(gè)方法和字段都有一個(gè)標(biāo)記,表明它是不是hide類型的。我們只要在jni層,把這個(gè)標(biāo)記改為不是hide的,就可以繞過(guò)檢查了。
然而,魔高一丈,道高一丈二。Google在Android P的正式版中勢(shì)必會(huì)推出更嚴(yán)厲的限制方案,到時(shí)候,又會(huì)有新的解決方案面世,讓我們拭目以待。
其實(shí),開(kāi)發(fā)者是無(wú)意和Google進(jìn)行技術(shù)對(duì)抗的,這是毫無(wú)意義的。泛濫成災(zāi)的修改導(dǎo)致了App大量的崩潰,Google實(shí)在看不下去了,所以才搞出這套限制方案;另一方面,插件化技術(shù)是剛需,尤其在中國(guó)的互聯(lián)網(wǎng)行業(yè),App崩潰會(huì)直接影響使用,很可能導(dǎo)致經(jīng)濟(jì)損失,所以開(kāi)發(fā)者才會(huì)不惜一切代價(jià)走插件化這條路。
再回到限制方案來(lái),Google也不是清一色不要開(kāi)發(fā)者使用系統(tǒng)底層的標(biāo)記為hide的API,而是推出了一組黑灰名單,如下所示:
名 單影 響light-greylist 淺灰名單僅打印警告日志,Google盡可能在未來(lái)版本提供 public APIdark-greylist 深灰名單第三方App不能訪問(wèn),開(kāi)發(fā)者可以申請(qǐng)把這份清單中的某些API加入到淺灰名單blacklist 黑名單第三方App不能訪問(wèn)所以,另一種應(yīng)對(duì)策略是,在插件化中使用淺灰名單中的API,比如說(shuō)ActivityThread的currentActivityThread方法。
Google的這組清單還在持續(xù)調(diào)整中,據(jù)我所知,給各大手機(jī)廠商的清單與其在社區(qū)中發(fā)布的清單略有出入。在Android P的正式版本中,這份清單會(huì)最終確定下來(lái)。所以現(xiàn)在中國(guó)的各個(gè)插件化框架的開(kāi)發(fā)人員,都在等Android P的正式版本發(fā)布后再制定相應(yīng)的策略。留給中國(guó)隊(duì)的時(shí)間不多了。
這本書的來(lái)龍去脈這是一本醞釀了3年的書。早在2015年Android插件化技術(shù)百家爭(zhēng)鳴時(shí),我就看好這個(gè)技術(shù),想寫一本書介紹這個(gè)技術(shù),但當(dāng)時(shí)的積累還不夠。那年,我在一場(chǎng)技術(shù)大會(huì)上發(fā)表了《Android插件化從入門到放棄》演講,四十五分鐘介紹了插件化技術(shù)的皮毛。后來(lái)這個(gè)演講內(nèi)容被整理成文章發(fā)布到網(wǎng)上,流傳很廣。
2017年1月,有企業(yè)要我去講2天Android插件化技術(shù)。為此,我花了一個(gè)月時(shí)間,準(zhǔn)備了四十多個(gè)例子。這是我第一次系統(tǒng)地積累了素材。
2017年6月,我在騰訊課堂做Android線上培訓(xùn),為了宣傳推廣我的課程,我寫了一系列文章《寫給Android App開(kāi)發(fā)人員看的Android底層知識(shí)》,共8篇,沒(méi)列太多代碼,完全以UML圖的方式向讀者普及Binder、AIDL、四大組件、AMS、PMS的知識(shí)。本書的第2章就是在這8篇文章的基礎(chǔ)之上進(jìn)行擴(kuò)充的。
2018年1月,我父親住院一周。我當(dāng)時(shí)在醫(yī)院每天晚上值班。老爺子半夜打呼嚕,吵得我睡不著,事后我才知道,我睡著了打呼嚕聲音比他還大。半夜睡不著時(shí)就開(kāi)始了本書的寫作,每晚堅(jiān)持寫到凌晨?jī)扇c(diǎn)。直到父親出院,這本書寫了將近五分之一。
碰巧的是,這一年5月底我結(jié)婚,促使我想在5月初完成這本書的一稿,為此,我宅在家里整整寫了3個(gè)月。僅以此書作為新婚禮物獻(xiàn)給我親愛(ài)的老婆,感謝你的理解,這本書才得以面世。
作者簡(jiǎn)介包建強(qiáng) 畢業(yè)于復(fù)旦大學(xué)數(shù)學(xué)系。先后在多家互聯(lián)網(wǎng)公司擔(dān)任無(wú)線部門技術(shù)總監(jiān),現(xiàn)在從事區(qū)塊鏈技術(shù)領(lǐng)域的研究,在Android、iOS、ReactNative等多門無(wú)線技術(shù)中跋涉過(guò),在App的項(xiàng)目管理上也有多年的實(shí)踐經(jīng)驗(yàn)。他曾經(jīng)出版了《App研發(fā)錄》,并有一個(gè)堅(jiān)持寫了10年的技術(shù)博客:http://jax.cnblogs.com/,他的GitHub地址:https://github.com/BaoBaoJianqiang。
目錄 Contents
序一
序二
序三
前言
第一部分 預(yù)備知識(shí)
第1章 插件化技術(shù)的昨天、今天與明天2
1.1 插件化技術(shù)是什么2
1.2 為什么需要插件化3
1.3 插件化技術(shù)的歷史3
1.4 插件化技術(shù)的用途到底是什么8
1.5 更好的替代品:React Native8
1.6 只有中國(guó)這么玩嗎9
1.7 四大組件都需要插件化技術(shù)嗎10
1.8 雙開(kāi)和虛擬機(jī)10
1.9 從原生頁(yè)面到HTML 5的過(guò)渡11
1.10 本章小結(jié)12
第2章 Android底層知識(shí)13
2.1 概述13
2.2 Binder原理14
2.3 AIDL原理16
2.4 AMS20
2.5 Activity工作原理21
2.5.1 App是怎么啟動(dòng)的21
2.5.2 啟動(dòng)App并非那么簡(jiǎn)單21
2.6 App內(nèi)部的頁(yè)面跳轉(zhuǎn)32
2.7 Context家族史34
2.8 Service工作原理36
2.8.1 在新進(jìn)程啟動(dòng)Service36
2.8.2 啟動(dòng)同一進(jìn)程的Service39
2.8.3 在同一進(jìn)程綁定Service39
2.9 BroadcastReceiver工作原理41
2.9.1 注冊(cè)過(guò)程43
2.9.2 發(fā)送廣播的流程44
2.9.3 廣播的種類45
2.10 ContentProvider工作原理46
2.10.1 ContentProvider的本質(zhì)49
2.10.2 匿名共享內(nèi)存(ASM)49
2.10.3 ContentProvider與AMS的通信流程50
2.11 PMS及App安裝過(guò)程52
2.11.1 PMS簡(jiǎn)介52
2.11.2 App的安裝流程52
2.11.3 PackageParser53
2.11.4 ActivityThread與PackageManager54
2.12 ClassLoader家族史55
2.13 雙親委托57
2.14 MultiDex57
2.15 實(shí)現(xiàn)一個(gè)音樂(lè)播放器App59
2.15.1 基于兩個(gè)Receiver的音樂(lè)播放器59
2.15.2 基于一個(gè)Receiver的音樂(lè)播放器63
2.16 本章小結(jié)68
第3章 反射70
3.1 基本反射技術(shù)70
3.1.1 根據(jù)一個(gè)字符串得到一個(gè)類70
3.1.2 獲取類的成員71
3.1.3 對(duì)泛型類的反射75
3.2 jOOR77
3.2.1 根據(jù)一個(gè)字符串得到一個(gè)類78
3.2.2 獲取類的成員78
3.2.3 對(duì)泛型類的反射79
3.3 對(duì)基本反射語(yǔ)法的封裝80
3.3.1 反射出一個(gè)構(gòu)造函數(shù)81
3.3.2 調(diào)用實(shí)例方法81
3.3.3 調(diào)用靜態(tài)方法82
3.3.4 獲取并設(shè)置一個(gè)字段的值82
3.3.5 對(duì)泛型類的處理83
3.4 對(duì)反射的進(jìn)一步封裝84
3.5 本章小結(jié)88
第4章 代理模式89
4.1 概述89
4.1.1 遠(yuǎn)程代理(AIDL)90
4.1.2 保護(hù)代理(權(quán)限控制)92
4.1.3 虛代理(圖片占位)92
4.1.4 協(xié)作開(kāi)發(fā)(Mock Class)92
4.1.5 給生活加點(diǎn)料(記日志)93
4.2 靜態(tài)代理和動(dòng)態(tài)代理94
4.3 對(duì)AMN的Hook95
4.4 對(duì)PMS的Hook97
4.5 本章小結(jié)98
第5章 對(duì)startActivity方法進(jìn)行Hook99
5.1 startActivity方法的兩種形式99
5.2 對(duì)Activity的startActivity方法進(jìn)行Hook100
5.2.1 方案1:重寫Activity的startActivityForResult方法102
5.2.2 方案2:對(duì)Activity的mInstrumentation字段進(jìn)行Hook102
5.2.3 方案3:對(duì)AMN的getDefault方法進(jìn)行Hook104
5.2.4 方案4:對(duì)H類的mCallback字段進(jìn)行Hook107
5.2.5 方案5:再次對(duì)Instrumentation字段進(jìn)行Hook109
5.3 對(duì)Context的startActivity方法進(jìn)行Hook111
5.3.1 方案6:對(duì)ActivityThread的mInstrumentation字段進(jìn)行Hook111
5.3.2 對(duì)AMN的getDafault方法進(jìn)行Hook是一勞永逸的113
5.4 啟動(dòng)沒(méi)有在AndroidManifest中聲明的Activity113
5.4.1 欺騙AMS的策略分析114
5.4.2 Hook的上半場(chǎng)115
5.4.3 Hook的下半場(chǎng):對(duì)H類的mCallback字段進(jìn)行Hook118
5.4.4 Hook的下半場(chǎng):對(duì)ActivityThread的mInstrumentation字段進(jìn)行Hook119
5.4.5 欺騙AMS的弊端121
5.5 本章小結(jié)121
第二部分 解決方案
第6章 插件化技術(shù)基礎(chǔ)知識(shí)124
6.1 加載外部的dex124
6.2 面向接口編程126
6.3 插件的瘦身129
6.4 對(duì)插件進(jìn)行代碼調(diào)試131
6.5 Application的插件化解決方案133
6.6 本章小結(jié)134
第7章 資源初探135
7.1 資源加載機(jī)制135
7.1.1 資源分類135
7.1.2 剪不斷理還亂:Resources和AssetManager136
7.2 資源的插件化解決方案137
7.3 換膚141
7.4 殊途同歸:另一種換膚方式149
7.5 本章小結(jié)149
第8章 最簡(jiǎn)單的插件化解決方案150
8.1 在AndroidManifest中聲明插件中的組件150
8.2 宿主App加載插件中的類151
8.3 啟動(dòng)插件Service152
8.4 加載插件中的資源152
8.5 本章小結(jié)154
第9章 Activity的插件化解決方案155
9.1 啟動(dòng)沒(méi)有在AndroidManifest中聲明的插件Activity155
9.2 基于動(dòng)態(tài)替換的Activity插件化解決方案159
9.2.1 Android啟動(dòng)Activity的原理分析159
9.2.2 故意命中緩存160
9.2.3 加載插件中類的方案1:為每個(gè)插件創(chuàng)建一個(gè)ClassLoader164
9.2.4 為了圓一個(gè)謊言而編造更多的謊言164
9.3 加載插件中類的方案2:合并多個(gè)dex166
9.4 為Activity解決資源問(wèn)題169
9.5 對(duì)LaunchMode的支持169
9.6 加載插件中類的方案3:修改App原生的ClassLoader172
9.7 本章小結(jié)174
第10章 Service的插件化解決方案175
10.1 Android界的荀彧和荀攸:Service和Activity175
10.2 預(yù)先占位176
10.3 startService的解決方案178
10.4 bindService的解決方案183
10.5 本章小結(jié)185
第11章 BroadcastReceiver的插件化解決方案186
11.