专栏首页Android相关Android AAPT1编译流程

Android AAPT1编译流程

简述

在APP打包过程中 , 会通过AAPT编译资源以及生成R.java文件. 一般我们使用以下命令来调用aapt命令 :

aapt package -f -M AndroidManifest.xml -S xxx -I TargetSdkPath -G /build/proguard/proguard.txt --auto-add-overlay

AAPT1编译流程

优化方向

  1. 尽量只让AAPT处理.9.png图片 , 其他图片使用Webp格式代替
    • 原因 : 因为AAPT会预处理PNG图片 , 会读取图片结构信息 , 从而进行压缩(Compress)
  2. 修改preProcessImages中 , WorkQueue的最大线程数
    • 原因 : 系统自带的AAPT中 , 处理PNG图片的最大线程数是4个 , 可以根据打包机器修改成8个或者10
  3. 去掉生成Proguard文件的步骤 , 直接在proguard.pro中写
    • 原因 : 由于aapt会根据AndroidManifest.xml中的字段去生成Proguard文件 , 需要读取该文件
  4. 尽量不要配置SplitConfiguration
    • 原因 : 因为会读取配置信息 , 并且进行分割

流程

  1. 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;
    }
}
  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;
}
  1. 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;
}
  1. writeResourceSymbols在处理完PNG图片并且编译完资源的XML文件后 , 开始写入R.java
status_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;
}
  1. 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);
}
  1. 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;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java线程池---addWorker方法解析

    以上是addWorker方法的注释,大致意思如下: 该方法是用来创建,运行,清理Workers的。 检查是否一个新的Worker能否能够添加到当前状态以及给...

    None_Ling
  • Java线程池---getTask方法解析

    /** * Performs blocking or timed wait for a task, depending on * current confi...

    None_Ling
  • ReentrantLock中的unlock流程

    在调用到ReentrantLock的unlock方法的时候,无论公平锁与非公平锁都会调用到sync.release(1)方法。

    None_Ling
  • 一年双非本科的大厂面试经历

    如findLastIndex([1,2,3,3,3,4,5], 3), 返回4。时间复杂度是多少?什么情况下时间复杂度最高?2. 请实现一个cacheReque...

    落落落洛克
  • 爬虫必须要了解的 JavaScript 混淆安全加固

    https://segmentfault.com/a/1190000019423501

    sergiojune
  • 不会吧不会吧,你不会还不知道这些提高JS代码质量的骚操作吧?

    程序的健壮性是指程序在执行时,在局部发生错误的情况下,不影响整个系统的运行,而且我们能够很快的定位到发生错误的位置。我们通常使用以下几种方式来保证程序的健壮性。

    AlbertYang
  • Python优雅写法,让你工作效率翻2倍

    我们都知道,Python 的设计哲学是「优雅」、「明确」、「简单」。这也许很多人选择 Python 的原因。但是我收到有些伙伴反馈,他写的 Python 并不优...

    马哥linux运维
  • 最新前端初中级面试题合集一,你确定不看一看嘛

    本文的面试题目是比较新的前端题目,适用于初中级的面试者,题目都是面试中高概率的题,也真诚的希望大家能够找到一个好的公司,希望这些真的对大家有用!谢谢,我会不断更...

    叫我可儿呀
  • Python-if-elif-else语句

    ================================================================

    阳光岛主
  • 深入理解Android Instant Run运行机制

    Instant Run Instant Run,是android studio2.0新增的一个运行机制,在你编码开发、测试或debug的时候,它都能显著减少你对...

    xiangzhihong

扫码关注云+社区

领取腾讯云代金券