源码查看

aapt的源码在所在的目录:Android/frameworks/base/tools/aapt/。

Main.cpp位置:android-6.0.0_r1/frameworks/base/tools/aapt/Main.cpp
首先查看Main.cpp的main方法:

int main(int argc, char* const argv[])
{char *prog = argv[0];Bundle bundle;bool wantUsage = false;int result = 1;    // pessimistically assume an error....//进行资源打包的参数p或packageelse if (argv[1][0] == 'p')bundle.setCommand(kCommandPackage);.../** Pull out flags.  We support "-fv" and "-f -v".*/while (argc && argv[0][0] == '-') {/* flag(s) found */const char* cp = argv[0] +1;while (*cp != '\0') {...//收集appt命令输入的参数,这些参数以"-"开头case '-':if (strcmp(cp, "-debug-mode") == 0) {bundle.setDebugMode(true);} ...break;default:fprintf(stderr, "ERROR: Unknown flag '-%c'\n", *cp);wantUsage = true;goto bail;}cp++;}argc--;argv++;}/** We're past the flags. The rest all goes straight in.*/bundle.setFileSpec(argv, argc);//根据bundle收集的参数进行资源处理result = handleCommand(&bundle);//输入参数错误时会跳转到此
bail:if (wantUsage) {usage();result = 2;}return result;
}
  • 1

在main函数内,首先创建一个Bundle对象,这个对象用来存储输入的操作类型和相关的参数。argv相当于java中的字符串数组。取该数组的第2个字符串的第一个char值。因为是执行资源打包,所以它是’p’。bundle记录执行类型为kCommandPackage,即资源打包。while循环处理剩余的char数组(即字符串数组),将参数按照类型设置到bundle中。参数解析完毕,则会执行handleCommand(&bundle)。如果在解析输入的参数时出现了错误,便使用goto跳转到bail代码块。在bail代码块中可能会执行usage();,这个方法会打印出aapt所有的命令类型和相关的参数。

先看看usage()函数,省略了与资源打包无关的信息。

void usage(void)
{...fprintf(stderr," %s p[ackage] [-d][-f][-m][-u][-v][-x][-z][-M AndroidManifest.xml] \\\n"" [-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile] \\\n"" [--debug-mode] [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n"" [--app-version VAL] [--app-version-name TEXT] [--custom-package VAL] \\\n"" [--rename-manifest-package PACKAGE] \\\n"" [--rename-instrumentation-target-package PACKAGE] \\\n"" [--utf16] [--auto-add-overlay] \\\n"" [--max-res-version VAL] \\\n"" [-I base-package [-I base-package ...]] \\\n"" [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n"" [-S resource-sources [-S resource-sources ...]] \\\n"" [-F apk-file] [-J R-file-dir] \\\n"" [--product product1,product2,...] \\\n"" [-c CONFIGS] [--preferred-density DENSITY] \\\n"" [--split CONFIGS [--split CONFIGS]] \\\n"" [--feature-of package [--feature-after package]] \\\n"" [raw-files-dir [raw-files-dir] ...] \\\n"" [--output-text-symbols DIR]\n"" [--apk-module moduleName]\n""\n"" Package the android resources. It will read assets and resources that are\n"" supplied with the -M -A -S or raw-files-dir arguments. The -J -P -F and -R\n"" options control which files are output.\n\n", gProgName);...
}
  • 1

打印appt执行的操作类型和对应的参数。

先看看handleCommand()函数,

int handleCommand(Bundle* bundle)
{switch (bundle->getCommand()) {case kCommandVersion:      return doVersion(bundle);...case kCommandPackage:      return doPackage(bundle);...default:fprintf(stderr, "%s: requested command not yet supported\n", gProgName);return 1;}
}
  • 1

在main函数中bundle设置的cmmand类型为kCommandPackage,所以会执行doPackage(bundle)代码。使用操作系统自带的搜索,检索aapt目录内的文件内容’doPackage’。会查找到doPackage()是Command.cpp中的函数。

Command.cpp位置:android-6.0.0_r1/frameworks/base/tools/aapt/Command.cpp
doPackage()函数中处理资源相关的代码:

int doPackage(Bundle* bundle)
{...// If they asked for any fileAs that need to be compiled, do so.if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {err = buildResources(bundle, assets, builder);if (err != 0) {goto bail;}}...
}
  • 1

继续搜索buildResources()函数所在的文件,查找到Resource.cpp。

Resource.cpp位置:android-6.0.0_r1/frameworks/base/tools/aapt/Resource.cpp
buildResources()函数中创建资源表相关的代码:

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);...
}
  • 1

继续查看ResourceTable.cpp的构造函数。

ResourceTable.cpp位置:android-6.0.0_r1/frameworks/base/tools/aapt/ResourceTable.cpp
ResourceTable构造函数

ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage, ResourceTable::PackageType type): mAssetsPackage(assetsPackage), mPackageType(type), mTypeIdOffset(0), mNumLocal(0), mBundle(bundle)
{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;}sp<Package> package = new Package(mAssetsPackage, packageId);mPackages.add(assetsPackage, package);mOrderedPackages.add(package);// Every resource table always has one first entry, the bag attributes.const SourcePos unknown(String8("????"), 0);getType(mAssetsPackage, String16("attr"), unknown);
}
  • 1

应用的资源id从0x7f开始,系统的资源id从0x01开始,共享类库的从0x00开始。如果我们想要自定义应用的资源id的起始值,则需要在switch结束后重新设置packageId值。这个自定义值可以Main.cpp的main函数中解析获取,并存放到bundle中。

修改源码

1,修改Bundle.h文件。
Bundle.h位置:android-6.0.0_r1/frameworks/base/tools/aapt/Bundle.h

添加如下代码:

class Bundle {
public:...//添加的获取和设置自定义id的函数const android::String8& getApkModule() const {return mApkModule;}void setApkModule(const char* str) { mApkModule=str;}...private:/* commands & modifiers */...//自定义idandroid::String8 mApkModule;...
}
  • 1

2,修改Main.cpp的main函数,添加解析自定义id的参数并设置到bundle。
代码如下:

int main(int argc, char* const argv[])
{...while (argc && argv[0][0] == '-') {...while (*cp != '\0') {...case '-':...//添加解析-apk-module参数信息} else if(strcmp(cp, "-apk-module") == 0){argc--;argv++;if (!argc) {fprintf(stderr, "ERROR: No argument supplied for '--apk-model' option\n");wantUsage = true;goto bail;}bundle.setApkModule(argv[0]);} else if (strcmp(cp, "-feature-of") == 0) {...break;...}...}...
}
  • 1

3,修改ResourceTable.cpp的构造函数,添加判断是否存在自定义id,如果存在,则修改packageId为自定义id。

ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage, ResourceTable::PackageType type)...
{ssize_t packageId = -1;switch (mPackageType) {...}//判断和设置自定义idif(!bundle->getApkModule().isEmpty()){android::String8 apkModuleVal = bundle->getApkModule();packageId = apkStringToInt(apkModuleVal);}sp<Package> package = new Package(mAssetsPackage, packageId);...
}//将字符串转换为ssize_t类型
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;
}
//转换为16进制
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;
}
  • 1

还需要在ResourceTable.h文件中声明apkStringToInt()和apkgetHex()函数。
ResourceTable.h位置:android-6.0.0_r1/frameworks/base/tools/aapt/ResourceTable.h
添加到public或private中。

    ssize_t apkStringToInt(const String8& s);uint32_t apkgetHex(char c, bool* outError);
  • 1

编译和修改错误

修改完毕,开启终端(或控制台)进入到android-6.0.0_r1目录,运行. build/envsetup.sh命令,配置运行环境。配置命令运行完毕,运行cd frameworks/base/tools/aapt,进入到aapt目录。执行mm命令,该命令会编译aapt模块中的代码,并生成可执行文件。运行结果如下:

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=6.0
TARGET_PRODUCT=full
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a
TARGET_CPU_VARIANT=generic
TARGET_2ND_ARCH=
TARGET_2ND_ARCH_VARIANT=
TARGET_2ND_CPU_VARIANT=
HOST_ARCH=x86_64
HOST_OS=darwin
HOST_OS_EXTRA=Darwin-16.0.0-x86_64-i386-64bit
HOST_BUILD_TYPE=release
BUILD_ID=MRA58K
OUT_DIR=out
============================================
host C++: aapt <= frameworks/base/tools/aapt/Main.cpp
host Executable: aapt (out/host/darwin-x86/obj/EXECUTABLES/aapt_intermediates/aapt)
clang: warning: argument unused during compilation: '-pie'
Install: out/host/darwin-x86/bin/aapt\e[0;32m#### make completed successfully (3 seconds) ####\e[00m
  • 1

生产的文件为aapt,在’android-6.0.0_r1/out/host/darwin-x86/bin/’目录中。
将aapt拷贝到指定目录,进入PluginDemo工程目录,使用aapt对工程进行资源打包,执行的命令如下:

../../../devTools/aapt/aapt package -f -m --apk-module 0x8f -J gen -S res -M AndroidManifest.xml -I ../../../devTools/android/android-sdk-macosx/platforms/android-23
/android.jar -F build/out/res.ap_
  • 1

该命令执行失败,某个资源id找不到对应的资源。错误信息:

res/layout/xxx.xml:9: error: Error: No resource found that matches the given name (at 'text' with value '@string/xxx').
  • 1

搜索相关的博客,在区长的专栏的Android aapt实现资源分区(补充携程aapt源码)这篇博客找到了出错的原因和解决方法。

继续看代码,

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 (*s == '@') {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 resourcesoutValue->data = rid;return true;}}}}if (accessor != NULL) {accessor->reportError(accessorCookie, "No resource found that matches the given name");}return false;}...
}
  • 1

在if(accessor)代码中对packageId进行验证。如果packageId不是ResourceTable构造函数中设置的3种类型,则出错,无法生成R.java文件和资源包。需要将ResourceTable构造函数中设置的packageId值存储下来,并在if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID )添加一个自定义id判断。

按照区长的博客添加头文件和cpp文件,并添加相应的代码。具体的代码请查看Android aapt实现资源分区(补充携程aapt源码)博客,本文不再写出。
1,创建文件
创建Help.h文件,存放在android-6.0.0_r1/frameworks/base/include/androidfw/。
创建Help.cpp文件,存放在android-6.0.0_r1/frameworks/base/libs/androidfw/。并将Help.cpp添加到android-6.0.0_r1/frameworks/base/libs/androidfw/Android.mk文件。
2,修改代码
在ResourceTable::ResourceTable()函数判断和设置自定义id代码后面添加使用Help记录packageId代码。
在ResTable::stringToValue()函数中添加对Help中记录的packageId判断代码。

重新编译aapt模块,获取新生成的aapt可执行文件。再次运行生成R.java和资源打包命令(与之前的命令相同),成功运行。

生成的R.java文件内容

package com.plugin.test;public final class R {public static final class attr {}public static final class drawable {public static final int p_icon_play=0x8f020000;}public static final class id {public static final int activity_main=0x8f070000;}public static final class layout {public static final int plugin_item=0x8f040000;}public static final class mipmap {public static final int p_icon_lock=0x8f030000;}public static final class string {public static final int app_name=0x8f050000;public static final int p_str=0x8f050001;}public static final class style {public static final int AppBaseTheme=0x8f060000;public static final int AppTheme=0x8f060001;}
}
  • 1

不再是原来系统默认的0x7f,都是自定义的0x8f。

继续执行命令编译代码,将class转换为dex,并将dex和资源包合并生成的apk。对apk签名,运行apk。apk正常运行

修改aapt和自定义资源ID相关推荐

  1. Android aar 包修改源代码和布局资源id

    Android aar 包修改源代码和布局资源id 原文地址 https://www.jianshu.com/p/bdb62f36bc62 注:本项目基于 Android Studio 讲解,Inte ...

  2. aapt2 适配之资源 id 固定

    前言 资源id的固定在热修复和插件化中极其重要.在热修复中,构建patch时,需要保持patch包的资源id和基线包的资源id一致:在插件化中,如果插件需要引用宿主的资源,则需要将宿主的资源id进行固 ...

  3. WordPress批量添加、修改、删除自定义字段的sql命令

    写一下WordPress批量添加.修改.删除自定义字段的sql命令,今天有个小伙伴问我一个问题,原本他用的是另外一个主题,看见日主题比较美观,于是他换到了日主题,用日主题的时候遇到了个问题,凡是资源站 ...

  4. operator-sdk实战开发K8S CRD自定义资源对象

    环境说明 系统:CentOS Linux release 7.6.1810 (Core) golang:v1.15 operator-sdk:v1.7.0 docker:v1.19 # 因为 oper ...

  5. android 指定资源id,Android 通过名称获取资源ID

    当我们获取网络数据的时候,解析之后往往都是一个字符串,而不是资源id,所有我们没有办法直接使用,只能通过名称来获取到资源id, package com.example.administrator.de ...

  6. Unity使用自定义资源(.asset)配置数据

    本文原创版权归 强哥的私房菜 所有,此处为转载,如有再转,请于篇首位置标明原创作者及出处,以示尊重! 作者:强哥的私房菜 原文:http://blog.csdn.net/liqiangeastsun/ ...

  7. 批量修改ip成自定义网址

    批量修改ip成自定义网址 针对ip后几位数修改成规定格式的网址 文本域进行添加多个ip <textarea rows="" cols="" id=&quo ...

  8. kubebuilder自定义资源

    github地址 一直在网上看k8s自定义资源这一块的内容,但是只停留于看,并没有真正的自己去实践一波,写这篇文章主要参考的是这篇博客,只是我对他做了一些简化,我只希望外部能够通过nodeip+por ...

  9. android 颜色 数组,在Android中声明维数,颜色,资源ID或其他非标准数据类型的数组?...

    我发现自己希望创建Android似乎本不支持的值的xml数组,并且正在寻找解决此问题的一些方法.当前,如果你希望在XML中声明一个值的数组,你仅限于字符串,整数和一个通用的非类型化"数组&q ...

最新文章

  1. Windows Phone实用开发技巧(32):照片角度处理
  2. ehcache_缓存基础
  3. 动态规划--连续子序列的最大和
  4. boost::push_relabel_max_flow用法的测试程序
  5. uboot移植(七)——移植三星官方uboot(一)
  6. HDU 4059 The Boss on Mars (容斥)(2011 Asia Dalian Regional Contest)
  7. linux xp镜像文件,让Windows XP镜像文件小一点儿(转)
  8. python求两数之和的命令_python计算两个数的百分比方法
  9. linux voip客户端,linux搭建VOIP
  10. POJ1149 最大流(Isap)
  11. 在 visual studio 中添加 ILDASM 工具
  12. 学 stm 32 单片机
  13. 如何在PowerPoint中添加幻灯片编号
  14. nacos 安装包下载 linux+windows
  15. 获取dom元素的方法
  16. win7自动关机方法
  17. java判断时间是否超过24小时_判断两个日期是否超过24小时!
  18. 工信部下属单位赛宝认证中心成功实施云服务平台
  19. 计算机丢失Uxteme,xp系统开机显示uxtheme.dll丢失的恢复教程
  20. 基于SSM的旅游网站

热门文章

  1. linux系统的电视怎么安装直播,Linux下nginx的安装及点直播使用示范
  2. python spark MLlib
  3. 2017福州大学面向对象程序设计课程作业六
  4. Ajax与Axios
  5. 灵魂战车2 复仇之魂
  6. APP的主要测试流程大全(新手必读)
  7. 动圈耳机振膜_动圈式耳机振膜技术
  8. 国外英文文化网站大全!
  9. 事务四大特性及实现原理
  10. 【动态规划】Lighting System Design 照明系统设计