Android aapt实现资源分区(补充携程aapt源码)
大致内容看这篇文章Android中如何修改编译的资源ID值(默认值是0x7F…可以随意改成0x02~0x7E),但是该文章中修改并不完全,是有问题的,见后文细说。该文章也参考了携程的aapt源码。
从该文章中看到修改的地方主要有这么几个地方。
- 加入- -apk-module 参数读取外部packageId值。
- 根据Bundle把packageId传入,并进行传递。
- 设置packageId的时候判断Bundle中是否有packageId,有的话覆盖packageId。
下面的代码基于Android源码主分支,应该是6.0的源码。
第一点修改的地方就在Main.cpp中。增加参数,具体位置自己找。
else if(strcmp(cp, "-apk-module") == 0){ argc--; argv++; if (!argc) { fprintf(stderr, "ERROR: No argument supplied for '--apk-module' option\n"); wantUsage = true; goto bail; } bundle.setApkModule(argv[0]);}
其次要在Bundle.h中增加setApkModule和getApkModule方法,当然还有一个成员变量,加在什么位置自行找。
android::String8 mApkModule;const android::String8& getApkModule() const {return mApkModule;}void setApkModule(const char* str) { mApkModule=str;}
在ResourceTable.cpp中读取bundle中的mApkModule,如果不为空则覆盖packageId。
//read the apk moduleif(!bundle->getApkModule().isEmpty()){ android::String8 apkmoduleVal=bundle->getApkModule(); packageId=apkStringToInt(apkmoduleVal);}
涉及到两个转换方法,其实现为。
ssize_t ResourceTable::apkStringToInt(const String8& s){ size_t i = 0; ssize_t val = 0; size_t len=s.length(); if (s[i] < '0' || s[i] > '9') { return -1; } // Decimal or hex? if (s[i] == '0' && s[i+1] == 'x') { i += 2; bool error = false; while (i < len && !error) { val = (val*16) + apkgetHex(s[i], &error); i++; } if (error) { return -1; } } else { while (i < len) { if (s[i] < '0' || s[i] > '9') { return false; } val = (val*10) + s[i]-'0'; i++; } } if (i == len) { return val; } return -1;}uint32_t ResourceTable::apkgetHex(char c, bool* outError){ if (c >= '0' && c <= '9') { return c - '0'; } else if (c >= 'a' && c <= 'f') { return c - 'a' + 0xa; } else if (c >= 'A' && c <= 'F') { return c - 'A' + 0xa; } *outError = true; return 0;}
此外还要在ResourceTable.h中声明这两个方法。
private: ssize_t apkStringToInt(const String8& s); uint32_t apkgetHex(char c, bool* outError);
执行编译,进入到android源码根目录。
make aapt
没有发生错误,则产生了aapt文件,文件在/out/host/linux-x86/bin。对应的windows版为/out/host/windows-x86/bin。mac版没有配置,所以不会产生,最好在mac上编译,以此类推,对应的文件应该在/out/host/darwin-x86/bin下。
这时候你使用aapt指定apk-module参数,肯定会报错。错误为。
error: Error: No resource found that matches the given name
所以最终的结论是携程开源的aapt源码并不完整。该错误在哪里报的呢。源码路径在/frameworks/base/libs/androidfw/ResourceTypes.cpp。该文件中有一个函数。
bool ResTable::stringToValue(Res_value* outValue, String16* outString, const char16_t* s, size_t len, bool preserveSpaces, bool coerceType, uint32_t attrID, const String16* defType, const String16* defPackage, Accessor* accessor, void* accessorCookie, uint32_t attrType, bool enforcePrivate) const{ //剩余代码 if (accessor) { uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, createIfNotFound); if (rid != 0) { if (kDebugTableNoisy) { ALOGI("Pckg %s:%s/%s: 0x%08x\n", String8(package).string(), String8(type).string(), String8(name).string(), rid); } uint32_t packageId = Res_GETPACKAGE(rid) + 1; if (packageId == 0x00) { outValue->data = rid; outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE; return true; } else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) { // We accept packageId's generated as 0x01 in order to support // building the android system resources outValue->data = rid; return true; } } } } if (accessor != NULL) { accessor->reportError(accessorCookie, "No resource found that matches the given name"); } return false; //剩余代码}
可以看到
if (accessor != NULL) { accessor->reportError(accessorCookie, "No resource found that matches the given name"); }
那么这个什么时候回执行到呢。就是当上面没有返回的情况下,前面返回的情况是当package为0x00,0x01,0x7f中的其中一个就会返回true,我们需要在else if分支加上我们的packageId的判断,让他返回true,但是这里没有Bundle,没办法传递packageId,因此我们需要一个辅助类Helper。
在/frameworks/base/include/androidfw/下新建Helper.h。这是一个单例。
#ifndef __Helper_h#define __Helper_h#include #include #include #include #include #include #include using namespace std;class Helper{ size_t packageId;public: static Helper* getInstance() { static Helper instance; return &instance; } size_t getPackageId(); void setPackageId(size_t _packageId);protected: struct Object_Creator { Object_Creator() { Helper::getInstance(); } }; static Object_Creator _object_creator; Helper() { packageId=0x7f; } ~Helper() { }};#endif
在/frameworks/base/libs/androidfw/下新建Helper.cpp。对方法进行实现。
#include #include #include using namespace std;Helper::Object_Creator Helper::_object_creator;size_t Helper::getPackageId(){ return packageId;}void Helper::setPackageId(size_t _packageId){ packageId=_packageId;}
同时修改该目录中的Android.mk文件。加入该cpp。
commonSources := \ 原来的内容 \ Helper.cpp
回到ResourceTable.cpp中,在获取packageId后,将值传入到Helper单例中去。
//引入头文件。#include <androidfw/Helper.h> //read the apk moduleif(!bundle->getApkModule().isEmpty()){ android::String8 apkmoduleVal=bundle->getApkModule(); packageId=apkStringToInt(apkmoduleVal); } //set package idHelper::getInstance()->setPackageId(packageId);
再回到刚才扔出异常的那个地方,在else if中加入packageId的判断。
//引入头文件。#include <androidfw/Helper.h>else if (packageId== Helper::getInstance()->getPackageId() ||packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID) { // We accept packageId's generated as 0x01 in order to support // building the android system resources outValue->data = rid; return true;}
只是加了一个条件
packageId== Helper::getInstance()->getPackageId()
再执行编译
make aapt
这时候就能随意指定packageId了,范围在0x00~0x7f之间,其中0x00、0x01、0x7f已经被使用。
之前报错的原因也有可能6.0的源码发生了变化,也有可能携程开源的代码不全。这个结论也不好下。当然我也不敢保证我这么修改后一定没有问题。
更多相关文章
- 基于MAC Android 8.1源码下载编译阅读
- android 调用系统文件管理器
- Android运行时权限,6.0—9.0多版本,多终端(手机,TV盒子)130行代码一劳
- Android屏幕锁定实例源码详解教程一
- 从零开始--系统深入学习android(实践-让我们开始写代码-Android框
- android 使用handler更新ui,使用与原理分析详解(附上代码以及截图)
- 如何编译Android内核源码
- Android 为【apk】文件签名,增加修改系统时间等权限