前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android AAPT1编译流程

Android AAPT1编译流程

作者头像
None_Ling
发布2020-11-04 10:11:54
1.4K0
发布2020-11-04 10:11:54
举报
文章被收录于专栏:Android相关Android相关

简述

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

代码语言:javascript
复制
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
代码语言:javascript
复制
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函数
代码语言:javascript
复制
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
代码语言:javascript
复制
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
代码语言:javascript
复制
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文件
代码语言:javascript
复制
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文件.
代码语言:javascript
复制
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;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简述
  • 优化方向
  • 流程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档