如何實現攜程動態加載插件中對aapt的改造

jopen 8年前發布 | 11K 次閱讀 Android開發 移動開發

原文出處:大頭鬼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文件中就可以了。

以上就是我個人的一些猜測,如有不對,請大家幫忙指正。


 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!