大致内容看这篇文章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 module
if(!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 <stdio.h>
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <stdlib.h>
#include <map>
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 <androidfw/Helper.h>
#include <iostream>
#include <string>
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 module
if(!bundle->getApkModule().isEmpty()){
android::String8 apkmoduleVal=bundle->getApkModule();
packageId=apkStringToInt(apkmoduleVal);
}
//set package id
Helper::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的源码发生了变化,也有可能携程开源的代码不全。这个结论也不好下。当然我也不敢保证我这么修改后一定没有问题。