在APP打包过程中 , 会通过AAPT编译资源以及生成R.java文件. 一般我们使用以下命令来调用aapt
命令 :
aapt package -f -M AndroidManifest.xml -S xxx -I TargetSdkPath -G /build/proguard/proguard.txt --auto-add-overlay
AAPT1编译流程
压缩
(Compress)preProcessImages
中 , WorkQueue
的最大线程数 4
个 , 可以根据打包机器修改成8
个或者10
个proguard.pro
中写 aapt
会根据AndroidManifest.xml中的字段去生成Proguard
文件 , 需要读取该文件SplitConfiguration
frameworks/base/tools/aapt/Main.cpp
中int main(int argc, char* const argv[]) { char *prog = argv[0]; Bundle bundle; bool wantUsage = false; int result = 1; // pessimistically assume an error. int tolerance = 0; // 设置Bundle的压缩方法 bundle.setCompressionMethod(ZipEntry::kCompressDeflated); ... // 检查命令行参数的第二个参数 , 按上面命令即`package` ... if (argv[1][0] == 'r') bundle.setCommand(kCommandRemove); else if (argv[1][0] == 'p') // 执行打包命令 bundle.setCommand(kCommandPackage); else if (argv[1][0] == 'c') bundle.setCommand(kCommandCrunch); ... } // 参数总数与参数index调整 argc -= 2; argv += 2; // 开始遍历参数列表以 - 开头的 , 也就是根据传入的命令行参数填充Bundle对象 while (argc && argv[0][0] == '-') { const char* cp = argv[0] +1; // 例如cp为 -I , -S , -G , -M while (*cp != '\0') { switch (*cp) { ... case 'c': // 更新参数index信息 argc--; argv++; ... // 添加配置参数 bundle.addConfigurations(argv[0]); break; case 'f': // 强制打包 bundle.setForce(true); break; case 'g': argc--; argv++; if (!argc) { fprintf(stderr, "ERROR: No argument supplied for '-g' option\n"); wantUsage = true; goto bail; } tolerance = atoi(argv[0]); bundle.setGrayscaleTolerance(tolerance); printf("%s: Images with deviation <= %d will be forced to grayscale.\n", prog, tolerance); break; ... #if 0 case 'p': bundle.setPseudolocalize(true); break; #endif ... case 'A': argc--; argv++; ... convertPath(argv[0]); bundle.addAssetSourceDir(argv[0]); break; case 'G': argc--; argv++; ... convertPath(argv[0]); bundle.setProguardFile(argv[0]); break; ... case 'I': argc--; argv++; if (!argc) { fprintf(stderr, "ERROR: No argument supplied for '-I' option\n"); wantUsage = true; goto bail; } convertPath(argv[0]); bundle.addPackageInclude(argv[0]); break; ... case 'M': argc--; argv++; if (!argc) { fprintf(stderr, "ERROR: No argument supplied for '-M' option\n"); wantUsage = true; goto bail; } convertPath(argv[0]); bundle.setAndroidManifestFile(argv[0]); break; ... case 'S': argc--; argv++; if (!argc) { fprintf(stderr, "ERROR: No argument supplied for '-S' option\n"); wantUsage = true; goto bail; } convertPath(argv[0]); bundle.addResourceSourceDir(argv[0]); break; } } // 设置参数的信息 bundle.setFileSpec(argv, argc); // 开始执行命令 , 并且传递bundle参数 , result = handleCommand(&bundle); bail: if (wantUsage) { usage(); result = 2; } return result; } int handleCommand(Bundle* bundle) { // 根据Bundle Command执行对应的函数 , 本次例子即为doPackage函数调用 switch (bundle->getCommand()) { case kCommandVersion: return doVersion(bundle); case kCommandList: return doList(bundle); case kCommandDump: return doDump(bundle); case kCommandAdd: return doAdd(bundle); case kCommandRemove: return doRemove(bundle); case kCommandPackage: return doPackage(bundle); case kCommandCrunch: return doCrunch(bundle); case kCommandSingleCrunch: return doSingleCrunch(bundle); case kCommandDaemon: return runInDaemonMode(bundle); default: fprintf(stderr, "%s: requested command not yet supported\n", gProgName); return 1; } }
framework/base/tools/aapt/Command.cpp
中 , 主要调用doPackage
函数int doPackage(Bundle* bundle) { const char* outputAPKFile; int retVal = 1; status_t err; sp<AaptAssets> assets; int N; FILE* fp; String8 dependencyFile; sp<ApkBuilder> builder; ... // 获取命令行参数个数 N = bundle->getFileSpecCount(); // 如果命令行参数小于1 , 资源路径目录总数为0 , Jar文件为0 , AndroidManifest路径为空 , // Asset路径目录总数为空的话 , 认为不需要编译 , 直接返回错误 if (N < 1 && bundle->getResourceSourceDirs().size() == 0 && bundle->getJarFiles().size() == 0 && bundle->getAndroidManifestFile() == NULL && bundle->getAssetSourceDirs().size() == 0) { fprintf(stderr, "ERROR: no input files\n"); goto bail; } // 获取输出的APK文件 outputAPKFile = bundle->getOutputAPKFile(); // 创建AaptAsset对象用来加载Asset文件 assets = new AaptAssets(); ... // 解析Bundle中的AndroidManifest.xml路径、Resouce路径,并添加到Assets对象中 err = assets->slurpFromArgs(bundle); if (err < 0) { goto bail; } ... // 创建ApkBuilder对象,用于APK到编译 builder = new ApkBuilder(configFilter); ... // 开始编译资源与AndroidManifest.xml文件 if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { err = buildResources(bundle, assets, builder); if (err != 0) { goto bail; } } ... // 更新Java中到符号 assets->applyJavaSymbols(); if (SourcePos::hasErrors()) { goto bail; } // 如果编译时有依赖的话 if (bundle->getGenDependencies()) { // 判断是否要输出APK文件 if (outputAPKFile) { dependencyFile = String8(outputAPKFile); // 添加.d的扩展符号 dependencyFile.append(".d"); } else { // 如果是生成R.java文件 , e.g. gen/com/foo/app/R.java.d dependencyFile = String8(bundle->getRClassDir()); dependencyFile.appendPath("R.java.d"); } } // Write out R.java constants if (!assets->havePrivateSymbols()) { // 如果通过--custom-pacakge来生成对应包名的资源的话 if (bundle->getCustomPackage() == NULL) { // 写入R.java文件e.g. gen/com/foo/app/R.java err = writeResourceSymbols(bundle, assets, assets->getPackage(), true, bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary()); } else { // 如果自定义了包名 , 就使用自定义包名 const String8 customPkg(bundle->getCustomPackage()); err = writeResourceSymbols(bundle, assets, customPkg, true, bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary()); } ... // 如果有额外的lib需要编译 , 则开始编译Lib的R文件 , 例如e.g. gen/com/foo/app/lib/R.java if (bundle->getExtraPackages() != NULL) { // Split on colon String8 libs(bundle->getExtraPackages()); char* packageString = strtok(libs.lockBuffer(libs.length()), ":"); while (packageString != NULL) { // Write the R.java file out with the correct package name err = writeResourceSymbols(bundle, assets, String8(packageString), true, bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary()); if (err < 0) { goto bail; } packageString = strtok(NULL, ":"); } libs.unlockBuffer(); } } else { // 生成package对应的R.java文件 err = writeResourceSymbols(bundle, assets, assets->getPackage(), false, false); if (err < 0) { goto bail; } err = writeResourceSymbols(bundle, assets, assets->getSymbolsPrivatePackage(), true, false); if (err < 0) { goto bail; } } // 输出Proguard文件 err = writeProguardFile(bundle, assets); if (err < 0) { goto bail; } // 输出主Dex的Proguard文件 err = writeMainDexProguardFile(bundle, assets); if (err < 0) { goto bail; } // Write the apk if (outputAPKFile) { // 如果是要生成APK的话 , 就会将资源添加到ApkBuilder中 err = addResourcesToBuilder(assets, builder); if (err != NO_ERROR) { goto bail; } const Vector<sp<ApkSplit> >& splits = builder->getSplits(); const size_t numSplits = splits.size(); for (size_t i = 0; i < numSplits; i++) { const sp<ApkSplit>& split = splits[i]; String8 outputPath = buildApkName(String8(outputAPKFile), split); // 生成APK err = writeAPK(bundle, outputPath, split); if (err != NO_ERROR) { fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string()); goto bail; } } } ... return retVal; }
buildResources
status_t pbuildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) { ... // 首先根据AndroidManifest.xml解析出Package、min-sdkversion等参数 status_t err = parsePackage(bundle, assets, androidManifestFile); if (err != NO_ERROR) { return err; } ... // 根据命令行参数判断当前资源类型 , 一般都是APP 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对象 ResourceTable table(bundle, String16(assets->getPackage()), packageType); // 将资源路径添加到asset对象中 err = table.addIncludedResources(bundle, assets); ... // 使用标准的XML格式 int xmlFlags = XML_COMPILE_STANDARD_RESOURCE; // 如果没有特别指定UTF16的话 , 就使用UTF8编码 if (!bundle->getUTF16StringsOption()) { xmlFlags |= XML_COMPILE_UTF8; } // 第一步 : 开始收集所有资源的信息 // resType -> leafName -> group KeyedVector<String8, sp<ResourceTypeSet> > *resources = new KeyedVector<String8, sp<ResourceTypeSet> >; // 收集资源路径 collect_files(assets, resources); // 创建各种类型资源的Set集合 sp<ResourceTypeSet> drawables; sp<ResourceTypeSet> layouts; sp<ResourceTypeSet> anims; sp<ResourceTypeSet> animators; sp<ResourceTypeSet> interpolators; sp<ResourceTypeSet> transitions; sp<ResourceTypeSet> xmls; sp<ResourceTypeSet> raws; sp<ResourceTypeSet> colors; sp<ResourceTypeSet> menus; sp<ResourceTypeSet> mipmaps; sp<ResourceTypeSet> fonts; // 设置资源路径 assets->setResources(resources); // now go through any resource overlays and collect their files sp<AaptAssets> current = assets->getOverlay(); while(current.get()) { KeyedVector<String8, sp<ResourceTypeSet> > *resources = new KeyedVector<String8, sp<ResourceTypeSet> >; current->setResources(resources); collect_files(current, resources); current = current->getOverlay(); } // 会将子目录下的文件也都添加到drawables中 // 例如res/drawable/drawable/drawable/string.xml文件也会添加到&xmls集合中 if (!applyFileOverlay(bundle, assets, &drawables, "drawable") || !applyFileOverlay(bundle, assets, &layouts, "layout") || !applyFileOverlay(bundle, assets, &anims, "anim") || !applyFileOverlay(bundle, assets, &animators, "animator") || !applyFileOverlay(bundle, assets, &interpolators, "interpolator") || !applyFileOverlay(bundle, assets, &transitions, "transition") || !applyFileOverlay(bundle, assets, &xmls, "xml") || !applyFileOverlay(bundle, assets, &raws, "raw") || !applyFileOverlay(bundle, assets, &colors, "color") || !applyFileOverlay(bundle, assets, &menus, "menu") || !applyFileOverlay(bundle, assets, &fonts, "font") || !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) { return UNKNOWN_ERROR; } bool hasErrors = false; // 如果drawables集合中存在资源文件 if (drawables != NULL) { if (bundle->getOutputAPKFile() != NULL) { // 如果要输出APK文件的话 err = preProcessImages(bundle, assets, drawables, "drawable"); } if (err == NO_ERROR) { err = makeFileResources(bundle, assets, &table, drawables, "drawable"); if (err != NO_ERROR) { hasErrors = true; } } else { hasErrors = true; } } if (mipmaps != NULL) { if (bundle->getOutputAPKFile() != NULL) { // 开始预处理PNG图片 , 内部会启动一个WorkQueue , 最多会有4个线程并行 // 执行PreProcessImageWorkUnit任务 , 只处理后缀为png的图片 , // 内部会读取PNG图片 , 处理.9.png等 // 具体代码在Image.cpp中 err = preProcessImages(bundle, assets, mipmaps, "mipmap"); } if (err == NO_ERROR) { // 将资源名生成资源ID(即Rid) , 并且存入ResourceTable中 , 用于后续生成R文件 err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap"); if (err != NO_ERROR) { hasErrors = true; } } else { hasErrors = true; } } // fronts、layouts、anims、animators、transitions、interpolators、xmls、raws // 文件夹中的资源也会同时加入ResourceTable中 , 也是调用makeFileResources方法 ... // 开始编译资源 current = assets; while(current.get()) { KeyedVector<String8, sp<ResourceTypeSet> > *resources = current->getResources(); // 获取Resouce名称对应的ResouceTypeSet对象 , 也就是上面的各种资源Set // 例如drawable、fronts、layouts等等 ssize_t index = resources->indexOfKey(String8("values")); if (index >= 0) { ResourceDirIterator it(resources->valueAt(index), String8("values")); ssize_t res; while ((res=it.next()) == NO_ERROR) { const sp<AaptFile>& file = it.getFile(); // 得到资源文件 , 调用compileResourceFile编译文件 // 其中会解析资源的XML , 包括attr、color、bool、integer等字段 // 包括declare_styleable、attr、item、string16、drawable、color等 // 然后解析每一个block中的id 、type、name等字段保存到Res_value中 res = compileResourceFile(bundle, assets, file, it.getParams(), (current!=assets), &table); if (res != NO_ERROR) { hasErrors = true; } } } current = current->getOverlay(); } if (colors != NULL) { err = makeFileResources(bundle, assets, &table, colors, "color"); if (err != NO_ERROR) { hasErrors = true; } } if (menus != NULL) { err = makeFileResources(bundle, assets, &table, menus, "menu"); if (err != NO_ERROR) { hasErrors = true; } } if (hasErrors) { return UNKNOWN_ERROR; } // 开始分配资源ID并且开始初始化资源Table if (table.hasResources()) { // 在这地方分配所有资源对应的ID err = table.assignResourceIds(); if (err < NO_ERROR) { return err; } } // 最后开始编译XML文件 , 因为有些资源会引用其他的资源 if (layouts != NULL) { ResourceDirIterator it(layouts, String8("layout")); while ((err=it.next()) == NO_ERROR) { String8 src = it.getFile()->getPrintableSource(); // 开始编译XML文件 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), it.getFile(), &table, xmlFlags); // Only verify IDs if there was no error and the file is non-empty. if (err == NO_ERROR && it.getFile()->hasData()) { ResXMLTree block; block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true); // 检查ID checkForIds(src, block); } else { hasErrors = true; } } } if (anims != NULL) { ResourceDirIterator it(anims, String8("anim")); while ((err=it.next()) == NO_ERROR) { // 编译anim的xml文件 err = compileXmlFile(bundle, assets, String16(it.getBaseName()), it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } } } // 后续会同上根据compileXmlFile来编译animator、interpolator、transition、xml、drawable、mipmap、color、menu、font的XML文件 ... // Now compile any generated resources. // 在XML编译过后 , 会开始编译资源 std::queue<CompileResourceWorkItem>& workQueue = table.getWorkQueue(); while (!workQueue.empty()) { CompileResourceWorkItem& workItem = workQueue.front(); int xmlCompilationFlags = xmlFlags | XML_COMPILE_PARSE_VALUES | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS; if (!workItem.needsCompiling) { xmlCompilationFlags &= ~XML_COMPILE_ASSIGN_ATTRIBUTE_IDS; xmlCompilationFlags &= ~XML_COMPILE_PARSE_VALUES; } err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.xmlRoot, workItem.file, &table, xmlCompilationFlags); if (err == NO_ERROR && workItem.file->hasData()) { assets->addResource(workItem.resPath.getPathLeaf(), workItem.resPath, workItem.file, workItem.file->getResourceType()); } else { hasErrors = true; } workQueue.pop(); } ... // 获取AndroidManifest文件 const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); String8 manifestPath(manifestFile->getPrintableSource()); // 开始生成最终的编译过后的AndroidManifest.xml文件 manifestFile->clearData(); // 开始解析AndroidManfest文件 sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); if (manifestTree == NULL) { return UNKNOWN_ERROR; } // 解析AndroidManifest.xml文件 , 将minSdkVersion、application、versionCode等参数设置到bundle中 err = massageManifest(bundle, &table, manifestTree); if (err < NO_ERROR) { return err; } // 编译manifest文件 err = compileXmlFile(bundle, assets, String16(), manifestTree, manifestFile, &table); if (table.modifyForCompat(bundle) != NO_ERROR) { return UNKNOWN_ERROR; } // 开始生成最终的Resouce Table ResTable finalResTable; sp<AaptFile> resFile; if (table.hasResources()) { sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); err = table.addSymbols(symbols, bundle->getSkipSymbolsWithoutDefaultLocalization()); if (err < NO_ERROR) { return err; } Vector<sp<ApkSplit> >& splits = builder->getSplits(); const size_t numSplits = splits.size(); // 根据分割配置生成resouces.arsc文件 , 尽量不要配置splite configuration ... // Perform a basic validation of the manifest file. This time we // parse it with the comments intact, so that we can use them to // generate java docs... so we are not going to write this one // back out to the final manifest data. // 开始执行一个基础的AndroidManifest校验 sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(), manifestFile->getGroupEntry(), manifestFile->getResourceType()); // 编译AndroidManifest文件 , 并且将相关的字段保存到outManifestFile对象中 err = compileXmlFile(bundle, assets, String16(), manifestFile, outManifestFile, &table, XML_COMPILE_STANDARD_RESOURCE & ~XML_COMPILE_STRIP_COMMENTS); if (err < NO_ERROR) { return err; } ResXMLTree block; block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true); // 初始化字段的常量 String16 manifest16("manifest"); String16 permission16("permission"); String16 permission_group16("permission-group"); String16 uses_permission16("uses-permission"); String16 instrumentation16("instrumentation"); String16 application16("application"); String16 provider16("provider"); String16 service16("service"); String16 receiver16("receiver"); String16 activity16("activity"); String16 action16("action"); String16 category16("category"); String16 data16("scheme"); String16 feature_group16("feature-group"); String16 uses_feature16("uses-feature"); const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789"; const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; const char* classIdentChars = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789$"; const char* processIdentChars = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:"; const char* authoritiesIdentChars = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-:;"; const char* typeIdentChars = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789:-/*+"; const char* schemeIdentChars = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-"; ResXMLTree::event_code_t code; sp<AaptSymbols> permissionSymbols; sp<AaptSymbols> permissionGroupSymbols; // 开始遍历AndroidManifest的block while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code > ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::START_TAG) { size_t len; if (block.getElementNamespace(&len) != NULL) { continue; } // 判断当前block是否为manifest if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { // 校验package属性 if (validateAttr(manifestPath, finalResTable, block, NULL, "package", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "sharedUserId", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { const bool isGroup = strcmp16(block.getElementName(&len), permission_group16.string()) == 0; if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "name", isGroup ? packageIdentCharsWithTheStupid : packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } SourcePos srcPos(manifestPath, block.getLineNumber()); sp<AaptSymbols> syms; if (!isGroup) { syms = permissionSymbols; if (syms == NULL) { sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("Manifest")); syms = permissionSymbols = symbols->addNestedSymbol( String8("permission"), srcPos); } } else { syms = permissionGroupSymbols; if (syms == NULL) { sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("Manifest")); syms = permissionGroupSymbols = symbols->addNestedSymbol( String8("permission_group"), srcPos); } } size_t len; ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); const char16_t* id = block.getAttributeStringValue(index, &len); if (id == NULL) { fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", manifestPath.string(), block.getLineNumber(), String8(block.getElementName(&len)).string()); hasErrors = true; break; } String8 idStr(id); char* p = idStr.lockBuffer(idStr.size()); char* e = p + idStr.size(); bool begins_with_digit = true; // init to true so an empty string fails while (e > p) { e--; if (*e >= '0' && *e <= '9') { begins_with_digit = true; continue; } if ((*e >= 'a' && *e <= 'z') || (*e >= 'A' && *e <= 'Z') || (*e == '_')) { begins_with_digit = false; continue; } if (isGroup && (*e == '-')) { *e = '_'; begins_with_digit = false; continue; } e++; break; } idStr.unlockBuffer(); // verify that we stopped because we hit a period or // the beginning of the string, and that the // identifier didn't begin with a digit. if (begins_with_digit || (e != p && *(e-1) != '.')) { fprintf(stderr, "%s:%d: Permission name <%s> is not a valid Java symbol\n", manifestPath.string(), block.getLineNumber(), idStr.string()); hasErrors = true; } syms->addStringSymbol(String8(e), idStr, srcPos); const char16_t* cmt = block.getComment(&len); if (cmt != NULL && *cmt != 0) { //printf("Comment of %s: %s\n", String8(e).string(), // String8(cmt).string()); syms->appendComment(String8(e), String16(cmt), srcPos); } syms->makeSymbolPublic(String8(e), srcPos); } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "name", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "name", classIdentChars, true) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "targetPackage", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "name", classIdentChars, false) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "permission", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "process", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "taskAffinity", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "name", classIdentChars, true) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "authorities", authoritiesIdentChars, true) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "permission", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "process", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "name", classIdentChars, true) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "permission", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "process", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "taskAffinity", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 || strcmp16(block.getElementName(&len), category16.string()) == 0) { if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "name", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "mimeType", typeIdentChars, true) != ATTR_OKAY) { hasErrors = true; } if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "scheme", schemeIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) { int depth = 1; while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code > ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::START_TAG) { depth++; if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) { ssize_t idx = block.indexOfAttribute( RESOURCES_ANDROID_NAMESPACE, "required"); if (idx < 0) { continue; } int32_t data = block.getAttributeData(idx); if (data == 0) { fprintf(stderr, "%s:%d: Tag <uses-feature> can not have " "android:required=\"false\" when inside a " "<feature-group> tag.\n", manifestPath.string(), block.getLineNumber()); hasErrors = true; } } } else if (code == ResXMLTree::END_TAG) { depth--; if (depth == 0) { break; } } } } } } if (resFile != NULL) { err = assets->addIncludedResources(resFile); if (err < NO_ERROR) { fprintf(stderr, "ERROR: Unable to parse generated resources, aborting.\n"); return err; } } return err; }
writeResourceSymbols
在处理完PNG图片并且编译完资源的XML文件后 , 开始写入R.javastatus_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, const String8& package, bool includePrivate, bool emitCallback) { const char* textSymbolsDest = bundle->getOutputTextSymbols(); String8 R("R"); const size_t N = assets->getSymbols().size(); for (size_t i=0; i<N; i++) { sp<AaptSymbols> symbols = assets->getSymbols().valueAt(i); String8 className(assets->getSymbols().keyAt(i)); String8 dest(bundle->getRClassDir()); dest.appendPath(className); dest.append(".java"); FILE* fp = fopen(dest.string(), "w+"); ... // 将symbols写入R.java文件中 status_t err = writeSymbolClass(fp, assets, includePrivate, symbols, className, 0, bundle->getNonConstantId(), emitCallback); if (textSymbolsDest != NULL && R == className) { String8 textDest(textSymbolsDest); textDest.appendPath(className); textDest.append(".txt"); FILE* fp = fopen(textDest.string(), "w+"); if (fp == NULL) { fprintf(stderr, "ERROR: Unable to open text symbol file %s: %s\n", textDest.string(), strerror(errno)); return UNKNOWN_ERROR; } if (bundle->getVerbose()) { printf(" Writing text symbols for class %s.\n", className.string()); } status_t err = writeTextSymbolClass(fp, assets, includePrivate, symbols, className); fclose(fp); if (err != NO_ERROR) { return err; } } ... return NO_ERROR; }
writeProguardFile
写入Proguard文件status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets) { status_t err = -1; if (!bundle->getProguardFile()) { return NO_ERROR; } ProguardKeepSet keep; // 写入AndroidManifest的Proguard err = writeProguardForAndroidManifest(&keep, assets, false); if (err < 0) { return err; } // 写入layout的proguard文件 err = writeProguardForLayouts(&keep, assets); if (err < 0) { return err; } return writeProguardSpec(bundle->getProguardFile(), keep, err); }
Package.cpp
中存在writeAPK
函数 , 主要用来执行打包 , 这个APK中只有资源并不会有Dex文件.status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet) { ZipFile* zip = NULL; int count; // 得到输出文件的类型 FileType fileType = getFileType(outputFile.string()); status_t status; zip = new ZipFile; // 打开Zip文件 status = zip->open(outputFile.string(), ZipFile::kOpenReadWrite | ZipFile::kOpenCreate); // 处理Asset文件 , 也就是通过zip->add将文件添加到Zip中 count = processAssets(bundle, zip, outputSet); ... // 处理Jar文件 count = processJarFiles(bundle, zip); // 中间还会删除一些Zip中的Entry ... // 将zip的缓冲区都输出到文件中 result = zip->flush(); if (result != NO_ERROR) { fprintf(stderr, "ERROR: Zip flush failed, archive may be hosed\n"); goto bail; } ... return result; }
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句