如何實現攜程動態加載插件中對aapt的改造
原文出處:大頭鬼Bruce的 http://blog.csdn.net/lzyzsd/article/details/49768283
前幾天,攜程無線部門開源了他們的插件框架,使用該框架可以方便的實現app的插件化開發和熱更新。
在陳博士發表的關于該框架的blog中,有這么一段
為aapt增加?apk-module參數。
如前所述,資源ID其實有一個PackageID的內部字段。我們為每個插件工程指定獨特的PackageID字段,這樣根據資源ID就很容易判明,此資源需要從哪個插件apk中去查找并加載了。在后文的資源加載部分會有進一步闡述。
很多同學都很關心這里應該怎么修改aapt來實現為不同的插件工程指定不同的PackageID,這里我來分析一下aapt的源碼,提供一個大概的思路吧。
個人才疏學淺,如有不對,還請攜程的同學指正一下。
appt相關的源碼都在framework/base/tools/aapt目錄下。
首先查看ResourceTable.cpp中的構造函數
ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage, ResourceTable::PackageType type) ssize_t packageId = -1; switch (mPackageType) { case App: case AppFeature: packageId = 0x7f; break; case System: packageId = 0x01; break; case SharedLibrary: packageId = 0x00; break; default: assert(0); break; }
資源的packageId就是在這里根據packageType來確定的,其中0x01是給系統資源使用的,在R.java中可以看到系統資源的id都是以0x01開頭的,0x7f是給我們的應用程序資源使用的,
同樣在R.java中,你可以看到,自己的app的資源id都是以0x7f開頭的。這也就是說0x01到0x7f之間的的值我們都可以作為packageId來用。
接下來我們看看是在哪里創建了ResourceTable對象。
打開Resource.cpp,buildResources方法,就是在這個方法中構造了resourceTable對象。
status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) ResourceTable::PackageType packageType = ResourceTable::App; if (bundle->getBuildSharedLibrary()) { packageType = ResourceTable::SharedLibrary; } else if (bundle->getExtending()) { packageType = ResourceTable::System; } else if (!bundle->getFeatureOfPackage().isEmpty()) { packageType = ResourceTable::AppFeature; } ResourceTable table(bundle, String16(assets->getPackage()), packageType);
可以看到這里是根據bundle的幾個方法的返回值,來確定生成的資源的packageId的。
那么,我們就可以在這里做一些手腳,來生成我們自己想要的packageId。我的做法就是給bundle對象加上一個getPackageId方法,
該方法會返回我們在命令行傳入的packageId。代碼類似下面
else if (!bundle->getFeatureOfPackage().isEmpty()) { packageType = ResourceTable::AppFeature; } else if (bundle->getPackageId() != 0) { packageType = bundle.getPackageId(); }
bundle類上還會定義一個setPackageId方法,用來保存packageId信息。
bundle對象是在Main.cpp的main方法,也就是appt程序的入口中構造出來的,下面列出一個代碼片段
switch (*cp) { case 'v': bundle.setVerbose(true); break; case 'a': bundle.setAndroidList(true); break;
bundle根據命令行傳入的各種參數,來設置自己的一些狀態,這里我們要加入自己的邏輯,來出來?apk-module參數,同時調用setPackageId方法就好了。
另外Dr.Chen的blog中還提到了
為aapt增加?public-R-path參數。
這里無非就是在Main.cpp中檢測到這個參數的時候,也保存到bundle中,然后在Resource.cpp的writeResourceSymbols方法中,
在生成 R.java的時候,把傳入的參數所指定的R.java中的變量插入到這個要生成的 R.java文件中就可以了。
以上就是我個人的一些猜測,如有不對,請大家幫忙指正。