什么是動(dòng)態(tài)加載垃沦?
- 簡(jiǎn)單來(lái)說(shuō)動(dòng)態(tài)加載可以加載原本apk不存在的代碼掸犬。
什么場(chǎng)景適用菜秦?
- 很多場(chǎng)景都適用异袄,比如可以實(shí)現(xiàn)app無(wú)感更新滚朵,不過(guò)有些應(yīng)用市場(chǎng)對(duì)含有動(dòng)態(tài)更新功能的app抓得比較嚴(yán)冤灾,因?yàn)閾?dān)心你偷偷更新了做些壞事。
動(dòng)態(tài)加載的難點(diǎn)是哪里辕近?
如何動(dòng)態(tài)加載res文件
- 如果只是做動(dòng)態(tài)加載java代碼韵吨,則只需要加載dex就行了,簡(jiǎn)單方便
- 如果要?jiǎng)討B(tài)加載的內(nèi)容包括res資源移宅,比如布局归粉,資源圖片椿疗。有兩種方案可選:
- 布局采用java代碼編寫(xiě),資源圖片放到assets中糠悼,這樣assets可以打包到j(luò)ar包中届榄,這樣就可以只加載dex,無(wú)需加載res倔喂,但是采用java代碼開(kāi)發(fā)res較為耗時(shí)不方便铝条,若是只有少量簡(jiǎn)單布局及資源圖片可能采用此方法
- 布局及資源文件等采用res的xml編寫(xiě),需要避免資源的id與宿主apk的id沖突以及如何將動(dòng)態(tài)加載的Resources注入到宿主Activity的Resources中滴劲,這樣就能夠通過(guò)宿主的
context.getResources().getIdentifier
接口獲取到資源id了
動(dòng)態(tài)更新需要怎么做攻晒?
加載dex
如何生成dex?
- 將java代碼打成jar包班挖,再用dex2jar工具將jar打成dex
- 需要注意鲁捏,要將R.class文件也打包進(jìn)入dex中,需要通過(guò)此文件關(guān)聯(lián)res資源萧芙,若無(wú)res資源可忽略
如何加載dex给梅?
- 通過(guò)DexClassLoader加載dex文件實(shí)例出ClassLoader
- 通過(guò)ClassLoader.loadClass("xx.xx.Xxx")
- 再通過(guò)反射的方式調(diào)用具體的方法
加載res
res打包成什么格式?
- apk格式双揪,可以在在需要?jiǎng)討B(tài)加載的項(xiàng)目中动羽,生成成apk后用壓縮工具打開(kāi),將里面的dex刪除即可
打包res需要注意什么渔期?
- 需要注意其中的資源id运吓,修改其id段,避免與資源id沖突疯趟,需要在gradle中配置
修改為package-id為0x70段的id, 默認(rèn)為0x7f拘哨,修改的id段不能大于0x7f段,否則會(huì)出問(wèn)題信峻,此方法只有在compileSdkVersion為28及以上才生效倦青,低于28的可以在網(wǎng)上尋找修改aapt源碼并編譯出新的功能用于修改package-id
android {
aaptOptions {
additionalParameters '--allow-reserved-package-id','--package-id','0x70'
}
}
如何加載res?
- 在成功加載dex代碼后盹舞,在加載dex運(yùn)行的第一時(shí)間進(jìn)行res的加載产镐,或者說(shuō)是將res注入到宿主中
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
String resFilePath = resFile(context).getAbsolutePath();
String baseApkPath = baseApkPath(context);
if (!TextUtils.isEmpty(baseApkPath)) {
LOG.i("重設(shè)原生資源--->" + baseApkPath);
addAssetPath.invoke(assetManager, baseApkPath);
}
LOG.i("設(shè)置外置資源resFilePath--->" + resFilePath);
addAssetPath.invoke(assetManager, resFilePath);
mResources =
new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources()
.getConfiguration());
mAssetManager = mResources.getAssets();
} catch (Exception ignored) {
ignored.printStackTrace();
}
public String baseApkPath(Context context) {
try {
String s = context.getApplicationContext().getPackageResourcePath();
if (s == null){
Process process = Runtime.getRuntime().exec("pm path " + context.getPackageName());
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
s = reader.readLine();
LOG.i("getRuntime baseApkPath",s);
}
LOG.i("getApplication baseApkPath",s);
return s.substring(s.indexOf("/"));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
加載so
如何加載so?
- 實(shí)例化DexClassLoader將so所在的路徑傳入即可自動(dòng)搜索加載
DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent)``librarySearchPath
即為so所以的路徑踢步,此路徑需要為當(dāng)前包所在的路徑data/data/package/
下癣亚,要注意的是,so會(huì)根據(jù)cpu的架構(gòu)不同而有不同的文件获印,所以我們?cè)谶x擇so時(shí)要先根據(jù)Build.CPU_ABI
判斷cpu的架構(gòu)選擇哪個(gè)so文件逃糟。
奈何表達(dá)能力太差,下次寫(xiě)個(gè)詳細(xì)的蓬豁,下次一定绰咽!