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 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的源码发生了变化,也有可能携程开源的代码不全。这个结论也不好下。当然我也不敢保证我这么修改后一定没有问题。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值