小言_互联网的博客

Android资源初探(一) 资源打包

298人阅读  评论(0)

Android中的资源也是一块比较重要的知识,平时工作中除了简单的使用context.getResouce().getColor(R.id.xxx)之外,我们也更想了解背后的原理。接下来的系列文章从资源编译、资源访问和具体实践几个部分来一窥Android中资源框架的基本原理。

后续更新计划,欢迎持续关注😝:

  • Android资源初探(二)运行时资源的访问
  • Android资源初探(三)换肤框架原理解析
  • Android资源初探(四)资源的插件化和热修复

apk文件

先来看一个简单的apk文件,解压后包含的文件如图所示:

可以看到一个apk文件解压后,主要分为3部分:

  1. 资源:包含res目录,assets目录,以及AndroidManifest.xmlresources.arsc都可以算作资源文件;
  2. 代码:包括classes.dexlib/xxx.so
  3. 签名信息META-INFO文件;
    可以认为 应用 = 可执行代码 + 资源,今天我们主要研究资源部分,后续有时间再来再探讨dex相关的话题。

apk中的资源类型

根据上面的分析,一个打包后的apk文件中属于资源的主要包含以下几部分:

  • res/layout/…、res/drawable…
  • assets/…
  • resources.arsc
  • AndroidManifest.xml

资源打包前后变化

我们可以写一个简单的hello world工程,然后打包后将apk文件进行解压,对比下上述这些资源文件在打包前后有何变化,如图所示:

  1. assets/.. 打包前后无变化
  2. res/layout/xxx.xml 打包后xml文件变成了二进制 res/values/…打包后不存在了 res/drawable-xxx/xxx 打包后drawable-xx后缀有变化,具体图片文件似乎没变化,
  3. AndroidManifest.xml 打包后,变成了二进制文件
  4. 无中生有了resources.arsc文件

可以看出apk打包过程中,类似布局文件之类的xml文件会被进行编译,而values下的文件会消失不见,同时也生成resources.arsc。这是我们的直观感受,接下来分析具体流程。

apk资源编译


上图是apk打包流程图,从流程图也可以看出资源的编译是通过aapt工具完成的,输入原始工程,输出为编译后的资源,同时生成R.java。先来看aapt。[AAPT]全名Android Asset Package Tool, 是官方SDK自带的资源打包工具,是一个可执行文件,具体在sdk/build-tools/&sdkversion/, 可以看到aapt是一个可执行程序,我们也可以通过将其加到环境变量中,然后直接在命令行中通过调用aapt命令来执行一些操作,具体命令参数可参考[https://developer.android.com/studio/command-line/aapt2];

总之就是我们完全可以通过手动输入aapt命令,来完成apk资源的编译,搜索aapt 手动打包相关介绍文章很多,这里不再赘述。

综上,android资源的打包就是调用aapt命令完成的,输入是当前工程,输出就是最终的apk资源文件,接下来我们来分析aapt是如何被调用起来的。

aapt调用逻辑

开发过程中执行一个assembleDebug task就能生成一个debug apk,由此我们也可以推测最终是在执行gradle task中完成aapt的调用。因为gradle源码较大,我们可以在工程中添加gradle依赖,分析jar包

因为gradle源码分析不是我们的重点,我们只需要了解它最终如何调起aapt即可,所以分析从简,我们在工程的build.gradle中使用

apply com.android.application
对应gradle AppPlugin.apply()方法==>BasePlugin.apply()
-> BasePlugin.java
public final void apply(Project project) {
      ...
		-> createAndroidTasks();
}
...
->AndroidBuilder.java
public void processResources(...) {
 ArrayList<String> command = Lists.newArrayList();
        //TODO:= findAAPT: sdk aapt
        String aapt = buildToolInfo.getPath(BuildToolInfo.PathId.AAPT);
        ...        

		//TODO:组件打包命令 sdk/build-tools/$version/aapt package xxxxxxx
        command.add(aapt);
        command.add("package");
        if (mVerboseExec) {
            command.add("-v");
        }
        command.add("-f");
        command.add("--no-crunch");
        // inputs
        command.add("-I");
        command.add(target.getPath(IAndroidTarget.ANDROID_JAR));
        command.add("-M");
        command.add(resPackageOutput);
		...
       //TODO:执行命令
        mCmdLineRunner.runCmdLine(command, null);
}

->CommandLineRunner.java
public void runCmdLine(String[] command) {
        // launch the command line process
        //TODO: Executes the specified string command in a separate process with the specified environment.
        Process process = Runtime.getRuntime().exec(command);
 }

通过调用链可以看出,gradle task最终生成一个AndroidBuilder,在其中构造出最终的aapt编译命令:
aapt --package -v xxxx -fxxx

到此,我们已经调用起来了aapt,接下来我们来分析aapt中完成资源编译的过程:

aapt资源编译流程

Aapt源码比较复杂,尤其ResourceTable, AaptFile, AaptAsset等C结构更是复杂,不是一篇文章能说清楚的。我已将分析后的源码上传至github,有兴趣的可以fork一下。以Main.cpp main函数为入口,相关调用链都已加注解(//TODO:)。[https://github.com/msandroid/androidsourceCode/blob/master/share/aapt/Main.cpp]
(img)
我们还是从简,主要是梳理下编译流程:

  1. 解析AndroidManifest.xml 得到包名,构造对应的ResourceTable对象,可以重点看下这个ResourceTable对象,可以这样理解,打包流程基本上就是构造ResourceTable过程,并利用这个数据结构来完成R.java和resources.arsc的生成。
  2. 引入外部资源,我们工程中会引入一些系统资源,比如R.color.white,他们都不在本工程中,而是在/system/framework/framework-res.apk中,所以在编译过程中需要将其引进来
  3. 收集需要编译的资源文件,构造aaptFile对象
  4. 给每个资源分配id
  5. 编译aaptfile资源
  6. 编译values资源
  7. 生成R.java
  8. 生成Resources.arsc文件
  9. 生成最终AndroidManifes.xml

可以看到aapt对xml资源进行了编译和压缩,一方面可以减少存储空间,将大量的重复字符串构造在一个公共的地方(resources.arsc),另一方面,这样在解析时候也能提高解析效率。反之,如果我们将这些资源原原本本打在apk文件中,其实从运行角度来说是不会有问题的。

R.java & resources.arsc

我们最后再来看下资源编译过程中生成的R.java和resource.arsc,两个文件的的关系如图所示

简单来说R.java中的id(0xPPTTEEEE)存在于resources.arsc文件中的id数组,然后id数组映射字符串常量池,即可找到id对应的值。

资源打包主要了解其关键流程,由于gradle和aapt对应源码细节较多,感兴趣的同学,可以参考[https://github.com/msandroid/androidsourceCode/tree/master/share/aapt]。


转载:https://blog.csdn.net/qwm8777411/article/details/100921576
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场