一、概述
什么是App的打包流程?
答:App的打包流程是指通过把资源(图片、文本)、源代码等资源打包成一个apk的过程。
了解打包流程能干什么事情?
通过了解打包流程我们可以对app打包的过程进行干涉,例如:1.在热更新项目中我们可以干涉R.java中资源id的生成来防止宿主App和插件App资源冲突。2.我们可以在编译前通过注解的形式生成辅助类从而优化代码量和代码结构(ButterKnife,Digger).3.通过javac把源代码编译成class文件后,在生成dex前我们可以对class文件进行干涉,动态的生成class文件(无痕埋点)。
下面就来看一下App的打包流程具体是怎样的。
二、打包流程
我借用包建强的《Android插件化开发》中的一张图来说明下
首先说下aapt是个什么东西?aapt是android中的资源打包工具,打包资源就用它。
下面看看打包流程的每一步都做了什么:
1.aapt把resources目录下的资源生成R.java文件 ,并为AndroidManifest.xml生成Manifest.java类。
2.aidl把项目中自定义的aidl文件生成对应的java类。
3.JavaCompiler把所有的Java源文件编译成class文件,包括:aapt生成的、aidl生成的、项目中自有的java源文件
4.使用proguard混淆,并生成一个proguardMapping.xml文件(可选项:可以混淆也可以不混淆)
5.使用dex工具把所有的class文件生成.dex文件
6.使用aapt资源打包工具把resources、assets目录下的资源打包成一个_ap文件
7.使用apkbuilder把所有的dex、_ap文件、AndroidManifest.xml文件打包成一个未签名的apk
8.使用jarsinger生成一个签名过的apk包
9.使用zipalign工具对要发布的apk文件进行对齐操作,以便在运行时节约内存。
以上就是生成一个Apk包需要经历的过程,大致可以分为9步。
三、修改aapt
假设你的项目正在使用插件化做热修复,你的项目可以简单的分为:宿主App、插件1、插件2、插件3四部分。而每个部分的资源都不一样。如果要把插件中的资源合并到宿主中就不得不考虑一个问题,那就是资源冲突的问题。一旦资源冲突app就加载不到想要的资源。怎么办?不着急,我们先来看看R.java的构成
public final class R { private R() {} public static final class attr { private attr() {} public static final int alpha = 0x7f020027; public static final int font = 0x7f020082; public static final int fontProviderAuthority = 0x7f020084; public static final int fontProviderCerts = 0x7f020085; public static final int fontProviderFetchStrategy = 0x7f020086; public static final int fontProviderFetchTimeout = 0x7f020087; public static final int fontProviderPackage = 0x7f020088; public static final int fontProviderQuery = 0x7f020089; public static final int fontStyle = 0x7f02008a; public static final int fontVariationSettings = 0x7f02008b; public static final int fontWeight = 0x7f02008c; public static final int ttcIndex = 0x7f020142; }
resources目录下的每一个资源都会对应一个资源ID。默认情况下资源的开头都是0x7f。
而资源id的构成分为三部分:
1.PackageId:apk包id,默认为0x7f
2.TypeId:资源类型Id ,例如:drawable、attar、string等
3.EntryId:资源类型对应的具体资源的Id
举个栗子:0x7f020027。其中0x7f是包ID,02是资源类型id,0027是资源id
其实如果我们想要解决资源冲突的问题就把packageid改掉就行了,宿主肯定是0x7f默认的,那我们把插件的改为0x71、0x72、0x73这种数值就行了,你们怎么办?
一种比较好用的办法是在aapt的源代码进行更改,在其把资源打包成0x7f之前,强制把0x7f改成我们传递过去的值。不过这种方式使用起来较为麻烦,需要修改并编译aapt的源代码,然后替换sdk目录下的那个aapt。
aapt是c++写的,所以我们只能找到c库。
1.找到aapt的源代码目录aapt文件夹
2.找到ResourceTable.cpp并打开,搜索0x7f,是的就是在这个地方做更改的。
3.新建一个gradle插件,把需要传递的值通过gradle传递过去。例如;0x71、0x72等。