2020 Android 大厂面试-插件化、模块化、组件化,移动开发工程师的岗位职责
替換了主工程context中LoadedApk的mResource對象
將新的Resource添加到主工程ActivityThread的mResourceManager中,并且根據(jù)Android版本做了不同處理
//第三步:關(guān)聯(lián)resource和Activity
Activity activity = mBase.newActivity(plugin.getClassLoader(), targetClassName, intent);
activity.setIntent(intent);
//設(shè)置Activity的mResources屬性,Activity中訪問資源時都通過mResources
ReflectUtil.setField(ContextThemeWrapper.class, activity, “mResources”, plugin.getResources());
資源沖突
資源id是由8位16進(jìn)制數(shù)表示,表示為0xPPTTNNNN, 由三部分組成:PackageId+TypeId+EntryId
修改aapt源碼,編譯期修改PP段。
修改resources.arsc文件,該文件列出了資源id到具體資源路徑的映射。
blog.csdn.net/jiangwei091…
// Main.cpp
result = handleCommand(&bundle);
case kCommandPackage: return doPackage(bundle);
// Command.cpp
int doPackage(Bundle* bundle) {
if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
err = buildResources(bundle, assets, builder);
if (err != 0) {
goto bail;
}
}
}
Resource.cpp
buildResources
ResourceTable.cpp
switch(mPackageType) {
case App:
case AppFeature:
packageId = 0x7f;
break;
case System:
packageId = 0x01;
break;
case SharedLibrary:
packageId = 0x00;
break;
}
首先找到入口類:Main.cpp:main函數(shù),解析參數(shù),然后調(diào)用handleCommand函數(shù)處理參數(shù)對應(yīng)的邏輯,我們看到了有一個函數(shù)doPackage。
然后就搜索到了Command.cpp:在他內(nèi)部的doPackage函數(shù)中進(jìn)行編譯工具的一個函數(shù):buildResources函數(shù),在全局搜索,發(fā)現(xiàn)了Resource.cpp:發(fā)現(xiàn)這里就是處理編譯工作,構(gòu)建ResourceTable的邏輯,在ResourceTable.cpp中,也是獲取PackageId的地方,下面我們就來看看如何修改呢?
其實最好的方法是,能夠修改aapt源碼,添加一個參數(shù),把我們想要編譯的PackageId作為輸入值,傳進(jìn)來最好了,那就是Bundle類型,他是從Main.cpp中的main函數(shù)傳遞到了最后的buildResources函數(shù)中,那么我們就可以把這個參數(shù)用Bundle進(jìn)行攜帶。
juejin.im/entry/5c008… www.jianshu.com/p/8d691b6bf…
————————————————————————————————————————————————
cloud.tencent.com/developer/a…
在整個過程中,需要修改到R文件、resources.arsc和二進(jìn)制的xml文件
四大組件支持
ProxyActivity代理
代理方式的關(guān)鍵總結(jié)起來有下面兩點:
ProxyActivity中需要重寫getResouces,getAssets,getClassLoader方法返回插件的相應(yīng)對象。生命周期函數(shù)以及和用戶交互相關(guān)函數(shù),如onResume,onStop,onBackPressedon,KeyUponWindow,FocusChanged等需要轉(zhuǎn)發(fā)給插件。
PluginActivity中所有調(diào)用context的相關(guān)的方法,如setContentView,getLayoutInflater,getSystemService等都需要調(diào)用ProxyActivity的相應(yīng)方法。
該方式有幾個明顯缺點:
插件中的Activity必須繼承PluginActivity,開發(fā)侵入性強(qiáng)。
如果想支持Activity的singleTask,singleInstance等launchMode時,需要自己管理Activity棧,實現(xiàn)起來很繁瑣。
插件中需要小心處理Context,容易出錯。
如果想把之前的模塊改造成插件需要很多額外的工作。
復(fù)制代碼
預(yù)埋StubActivity,hook系統(tǒng)啟動Activity的過程
VirtualAPK通過替換了系統(tǒng)的Instrumentation,hook了Activity的啟動和創(chuàng)建,省去了手動管理插件Activity生命周期的繁瑣,讓插件Activity像正常的Activity一樣被系統(tǒng)管理,并且插件Activity在開發(fā)時和常規(guī)一樣,即能獨(dú)立運(yùn)行又能作為插件被主工程調(diào)用。
其他插件框架在處理Activity時思想大都差不多,無非是這兩種方式之一或者兩者的結(jié)合。在hook時,不同的框架可能會選擇不同的hook點。如360的RePlugin框架選擇hook了系統(tǒng)的ClassLoader,即構(gòu)造Activity2的ClassLoader,在判斷出待啟動的Activity是插件中的時,會調(diào)用插件的ClassLoader構(gòu)造相應(yīng)對象。另外RePlugin為了系統(tǒng)穩(wěn)定性,選擇了盡量少的hook,因此它并沒有選擇hook系統(tǒng)的startActivity方法來替換intent,而是通過重寫Activity的startActivity,因此其插件Activity是需要繼承一個類似PluginActivity的基類的。不過RePlugin提供了一個Gradle插件將插件中的Activity的基類換成了PluginActivity,用戶在開發(fā)插件Activity時也是沒有感知的。
www.jianshu.com/p/ac96420fc…
Service插件化總結(jié)
初始化時通過ActivityManagerProxy Hook住了IActivityManager。
服務(wù)啟動時通過ActivityManagerProxy攔截,判斷是否為遠(yuǎn)程服務(wù),如果為遠(yuǎn)程服務(wù),啟動RemoteService,如果為同進(jìn)程服務(wù)則啟動LocalService。
如果為LocalService,則通過DexClassLoader加載目標(biāo)Service,然后反射調(diào)用attach方法綁定Context,然后執(zhí)行Service的onCreate、onStartCommand方法
如果為RemoteService,則先加載插件的遠(yuǎn)程Service,后續(xù)跟LocalService一致。
3.模塊化實現(xiàn)(好處,原因)
www.cnblogs.com/Jackie-zhan…
1、模塊間解耦,復(fù)用。
(原因:對業(yè)務(wù)進(jìn)行模塊化拆分后,為了使各業(yè)務(wù)模塊間解耦,因此各個都是獨(dú)立的模塊,它們之間是沒有依賴關(guān)系。
每個模塊負(fù)責(zé)的功能不同,業(yè)務(wù)邏輯不同,模塊間業(yè)務(wù)解耦。模塊功能比較單一,可在多個項目中使用。)
2、可單獨(dú)編譯某個模塊,提升開發(fā)效率。
(原因:每個模塊實際上也是一個完整的項目,可以進(jìn)行單獨(dú)編譯,調(diào)試)
3、可以多團(tuán)隊并行開發(fā),測試。
原因:每個團(tuán)隊負(fù)責(zé)不同的模塊,提升開發(fā),測試效率。
組件化與模塊化
組件化是指以重用化為目的,將一個系統(tǒng)拆分為一個個單獨(dú)的組件
避免重復(fù)造輪子,節(jié)省開發(fā)維護(hù)成本;
降低項目復(fù)雜性,提升開發(fā)效率;
多個團(tuán)隊公用同一個組件,在一定層度上確保了技術(shù)方案的統(tǒng)一性。
模塊化業(yè)務(wù)分層:由下到上
基礎(chǔ)組件層:
底層使用的庫和封裝的一些工具庫(libs),比如okhttp,rxjava,rxandroid,glide等
業(yè)務(wù)組件層:
與業(yè)務(wù)相關(guān),封裝第三方sdk,比如封裝后的支付,即時通行等
業(yè)務(wù)模塊層:
按照業(yè)務(wù)劃分模塊,比如說IM模塊,資訊模塊等
Library Module開發(fā)問題
在把代碼抽取到各個單獨(dú)的Library Module中,會遇到各種問題。
最常見的就是R文件問題,Android開發(fā)中,各個資源文件都是放在res目錄中,在編譯過程中,會生成R.java文件。
R文件中包含有各個資源文件對應(yīng)的id,這個id是靜態(tài)常量,但是在Library Module中,這個id不是靜態(tài)常量,那么在開發(fā)時候就要避開這樣的問題。
舉個常見的例子,同一個方法處理多個view的點擊事件,有時候會使用switch(view.getId())這樣的方式,
然后用case R.id.btnLogin這樣進(jìn)行判斷,這時候就會出現(xiàn)問題,因為id不是經(jīng)常常量,那么這種方式就用不了。
4.熱修復(fù)、插件化
www.jianshu.com/p/704cac3eb…
宿主: 就是當(dāng)前運(yùn)行的APP
插件: 相對于插件化技術(shù)來說,就是要加載運(yùn)行的apk類文件
補(bǔ)丁: 相對于熱修復(fù)技術(shù)來說,就是要加載運(yùn)行的.patch,.dex,*.apk等一系列包含dex修復(fù)內(nèi)容的文件。
QQ 空間超級補(bǔ)丁方案
Tinker
HotFix
當(dāng)然就熱修復(fù)的實現(xiàn),各個大廠還有各自的實現(xiàn),比如餓了嗎的Amigo,美團(tuán)的Robust,實現(xiàn)及優(yōu)缺點各有差異,但總的來說就是兩大類
ClassLoader 加載方案
Native層替換方案
或者是參考Android Studio Instant Run 的思路實現(xiàn)代碼整體的增量更新。但這樣勢必會帶來性能的影響。
Sophix
www.jianshu.com/p/4d30ce3e5…
底層替換方案
原理:在已經(jīng)加載的類中直接替換掉原有方法,是在原有類的結(jié)構(gòu)基礎(chǔ)上進(jìn)行修改的。在hook方法入口ArtMethod時,通過構(gòu)造一個新的ArtMethod實現(xiàn)替換方法入口的跳轉(zhuǎn)。
應(yīng)用:能即時生效,Andfix采用此方案。
缺點:底層替換穩(wěn)定性不好,適用范圍存在限制,通過改造代碼繞過限制既不優(yōu)雅也不方便,并且還沒提供資源及so的修復(fù)。
類加載方案
原理:讓app重新啟動后讓ClassLoader去加載新的類。如果不重啟,原來的類還在虛擬機(jī)中無法重復(fù)加載。
優(yōu)點:修復(fù)范圍廣,限制少。
應(yīng)用:騰訊系包括QQ空間,手QFix,Tinker采用此方案。
QQ空間會侵入打包流程。
QFix需要獲取底層虛擬機(jī)的函數(shù),不穩(wěn)定。
Tinker是完整的全量dex加載。
Tinker與Sophix方案不同之處
Tinker采用dex merge生成全量DEX方案。反編譯為smali,然后新apk跟基線apk進(jìn)行差異對比,最后得到補(bǔ)丁包。
Dalvik下Sophix和Tinker相同,在Art下,Sophix不需要做dex merge,因為Art下本質(zhì)上虛擬機(jī)已經(jīng)支持多dex的加載,要做的僅僅是把補(bǔ)丁dex作為主dex(classes.dex)加載而已:
將補(bǔ)丁dex命名為classes.dex,原apk中的dex依次命名為classes(2, 3, 4…).dex就好了,然后一起打包為一個壓縮文件。然后DexFile.loadDex得到DexFile對象,最后把該DexFile對象整個替換舊的dexElements數(shù)組就好了。
資源修復(fù)方案
基本參考InstantRun的實現(xiàn):構(gòu)造一個包含所有新資源的新的AssetManager。并在所有之前引用到原來的AssetManager通過反射替換掉。
Sophix不修改AssetManager的引用,構(gòu)造的補(bǔ)丁包中只包含有新增或有修改變動的資源,在原AssetManager中addAssetPath這個包就可以了。資源包不需要在運(yùn)行時合成完整包。
so庫修復(fù)方案
本質(zhì)是對native方法的修復(fù)和替換。類似類修復(fù)反射注入方式,將補(bǔ)丁so庫的路徑插入到nativeLibraryDirectories數(shù)據(jù)最前面。
Method Hook
www.jianshu.com/p/7dcb32f8a… pqpo.me/2017/07/07/…
5.項目組件化的理解
juejin.im/post/5b5f17…
總結(jié)
組件化相較于單一工程,在組件模式下可以提高編譯速度,方便單元測試,提高開發(fā)效率。
開發(fā)人員分工更加明確,基本上做到互不干擾。
業(yè)務(wù)組件的架構(gòu)也可以自由選擇,不影響同伴之間的協(xié)作。
降低維護(hù)成本,代碼結(jié)構(gòu)更加清晰。
6.描述清點擊 Android Studio 的 build 按鈕后發(fā)生了什么
blog.csdn.net/u011026779/… blog.csdn.net/github_3713…
apply plugin : ‘com.android.application’
apply plugin : ‘com.android.library’
編譯五階段
1.準(zhǔn)備依賴包 Preparation of dependecies
2.合并資源并處理清單 Merging resources and proccesssing Manifest
3.編譯 Compiling
4.后期處理 Postprocessing
5.包裝和出版 Packaging and publishing
簡單構(gòu)建流程:
1. Android編譯器(5.0之前是Dalvik,之后是ART)將項目的源代碼(包括一些第三方庫、jar包和aar包)轉(zhuǎn)換成DEX文件,將其他資源轉(zhuǎn)換成已編譯資源。
2. APK打包器將DEX文件和已編譯資源在使用秘鑰簽署后打包。
3. 在生成最終 APK 之前,打包器會使用zipalign 等工具對應(yīng)用進(jìn)行優(yōu)化,減少其在設(shè)備上運(yùn)行時的內(nèi)存占用。
構(gòu)建流程結(jié)束后獲得測試或發(fā)布用的apk。
復(fù)制代碼
圖中的矩形表示用到或者生成的文件,橢圓表示工具。
1. 通過aapt打包res資源文件,生成R.java、resources.arsc和res文件
2. 處理.aidl文件,生成對應(yīng)的Java接口文件
3. 通過Java Compiler編譯R.java、Java接口文件、Java源文件,生成.class文件
4. 通過dex命令,將.class文件和第三方庫中的.class文件處理生成classes.dex
5. 通過apkbuilder工具,將aapt生成的resources.arsc和res文件、assets文件和classes.dex一起打包生成apk
6. 通過Jarsigner工具,對上面的apk進(jìn)行debug或release簽名
7. 通過zipalign工具,將簽名后的apk進(jìn)行對齊處理。
這樣就得到了一個可以安裝運(yùn)行的Android程序。
復(fù)制代碼
7.徹底搞懂Gradle、Gradle Wrapper與Android Plugin for Gradle的區(qū)別和聯(lián)系
zhuanlan.zhihu.com/p/32714369 blog.csdn.net/LVXIANGAN/a…
Offline work時可能出現(xiàn)"No cached version of com.android.tools.build:gradle:xxx available for offline mode"問題
Gradle: gradle-wrapper.properties中的distributionUrl=https/😕/services.gradle.org/distributions/gradle-2.10-all.zip
Gradle插件:build.gradle中依賴的classpath ‘com.android.tools.build:gradle:2.1.2’
Gradle:
一個構(gòu)建系統(tǒng),構(gòu)建項目的工具,用來編譯Android app,能夠簡化你的編譯、打包、測試過程。
Gradle是一個基于Apache Ant和Apache Maven概念的項目自動化建構(gòu)工具。它使用一種基于Groovy的特定領(lǐng)域語言來聲明項目設(shè)置,而不是傳統(tǒng)的XML。當(dāng)前其支持的語言限于Java、Groovy和Scala
最后
我見過很多技術(shù)leader在面試的時候,遇到處于迷茫期的大齡程序員,比面試官年齡都大。這些人有一些共同特征:可能工作了7、8年,還是每天重復(fù)給業(yè)務(wù)部門寫代碼,工作內(nèi)容的重復(fù)性比較高,沒有什么技術(shù)含量的工作。問到這些人的職業(yè)規(guī)劃時,他們也沒有太多想法。
其實30歲到40歲是一個人職業(yè)發(fā)展的黃金階段,一定要在業(yè)務(wù)范圍內(nèi)的擴(kuò)張,技術(shù)廣度和深度提升上有自己的計劃,才有助于在職業(yè)發(fā)展上有持續(xù)的發(fā)展路徑,而不至于停滯不前。
不斷奔跑,你就知道學(xué)習(xí)的意義所在!
以上進(jìn)階BATJ大廠學(xué)習(xí)資料可以免費(fèi)分享給大家,需要完整版的朋友,【點這里可以看到全部內(nèi)容】。
總結(jié)
以上是生活随笔為你收集整理的2020 Android 大厂面试-插件化、模块化、组件化,移动开发工程师的岗位职责的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三星emcp型号详解_eMMC UFS
- 下一篇: Android AspectJ详解