文章目录
前言
一、点击Run背后的秘密?
1.1 Run背后的Gradle tasks
1.2 Tasks名称
1.3 部分Tasks详细说明
二、Android Gradle Plugin和AS之间的关系
1.Android Gradle Plugin 核心源码分析
三、全面的Android构建流程
3.1 新版本的Android构建流程
3.2 旧版本的Android构建流程
3.3:aapt
3.4:aidl
3.5:Java 源码编译
3.4:dex
3.6:apkbuilder
3.7:Jarsigner
3.8:zipalign
3.9 APK 构建更详细流程
四 AS源码分析
4.1 核心代码
编辑
4.2 检查项目和读取基本配置
4.3 RunStats
4.4 执行编译之前的准备工作
4.5 安装部署
五 总结
关键词:Android studio run背后的秘密,android编译流程
提示:可能需要大概30分钟阅读时间
背景:平时在开发过程中,大部人人都是通过点击Run' 按钮,运行app,Android Studio 就会开动,代码奇迹般地变成 APK,被安装到手机上,显示 APP 的界面。背后发生了什么?
要搞清楚这个问题必现需要明白Gradle的构建流程,下面看下Gradle构建流程
Gradle生命周期分为三个阶段,分别是Initialization(初始化阶段),Configuration(配置阶段),和Execution(执行阶段),而执行阶段会执行一系列task,进行主要的构建工作。
那么自然Run按钮背后的构建工作也是由一系列task组成的,那么我们有办法去查看这些task都有哪些么?Android Studio提供了强大的日志记录功能,主要需要进行以下3步工作:
通过上图可以看到点击Build以后总体的流程分为
1.1.1.1 Load buid
1.1.1.2 Config build
1.1.1.3 Calculate task graph
1.1.1.4 run task
那么我们把上面的展开以后看下具体做了些什么?
1.1.2 展开后
此时如果我们点击Run按钮以后,我们看下会做什么
,通过观察我们发现就是把之前的build流程走一遍,然后增加了install,也就是说把编译好的Apk, 安装到用户手机上面了
completed successfully 1 s 382 ms
Run build 981 ms
Load build 3 ms
Evaluate settings 2 ms
Finalize build cache configuration
Configure build 122 ms
Load projects 2 ms
Calculate task graph 148 ms
Run tasks 598 ms
:app:buildInfoDebugLoader 9 ms
:app:preBuild 1 ms
:app:preDebugBuild 10 ms
:app:compileDebugAidl
:app:checkDebugManifest 1 ms
:app:compileDebugRenderscript
:app:generateDebugBuildConfig 2 ms
:app:prepareLintJar
:app:generateDebugSources
:app:javaPreCompileDebug 21 ms
:app:mainApkListPersistenceDebug
:app:generateDebugResValues 3 ms
:app:generateDebugResources
:app:mergeDebugResources 97 ms
:app:createDebugCompatibleScreenManifests 2 ms
:app:processDebugManifest 72 ms
:app:processDebugResources 25 ms
:app:compileDebugJavaWithJavac 27 ms
:app:instantRunMainApkResourcesDebug 1 ms
:app:mergeDebugShaders 2 ms
:app:compileDebugShaders 3 ms
:app:generateDebugAssets
:app:mergeDebugAssets 3 ms
:app:validateSigningDebug
:app:signingConfigWriterDebug 1 ms
:app:processInstantRunDebugResourcesApk 1 ms
:app:checkManifestChangesDebug 5 ms
:app:checkDebugDuplicateClasses 2 ms
:app:transformClassesWithExtractJarsForDebug 1 ms
:app:transformClassesWithInstantRunVerifierForDebug 26 ms
:app:transformClassesWithDependencyCheckerForDebug 9 ms
:app:mergeDebugJniLibFolders 1 ms
:app:processDebugJavaRes
:app:transformNativeLibsWithMergeJniLibsForDebug 8 ms
:app:transformResourcesWithMergeJavaResForDebug 8 ms
:app:transformNativeLibsAndResourcesWithJavaResourcesVerifierForDebug 1 ms
:app:transformClassesWithInstantRunForDebug 24 ms
:app:transformClassesAndClassesEnhancedWithInstantReloadDexForDebug 14 ms
:app:incrementalDebugTasks 1 ms
:app:preColdswapDebug 1 ms
:app:fastDeployDebugExtractor 1 ms
:app:generateDebugInstantRunAppInfo 2 ms
:app:transformClassesWithDexBuilderForDebug 31 ms
:app:transformDexArchiveWithExternalLibsDexMergerForDebug 3 ms
:app:transformDexArchiveWithDexMergerForDebug 5 ms
:app:transformDexWithInstantRunDependenciesApkForDebug 2 ms
:app:instantRunSplitApkResourcesDebug 3 ms
:app:transformDexWithInstantRunSlicesApkForDebug 2 ms
:app:packageDebug 10 ms
:app:buildInfoGeneratorDebug 12 ms
:app:compileDebugSources 1 ms
:app:assembleDebug
上述tasks大概可分为五个阶段:
a. 调用 aapt 生成项目和所有 aar 依赖的R.java b. 生成资源索引文件 c. 输出符号表
有了以上的初步了解之后,再来看看 Android Gradle Plugin 是如何实现构建流程的。
我们使用的 apply plugin: 'com.android.application' 和 apply plugin:'com.android.library' 插件对应的类分别是 AppPlugin 和 LibraryPlugin。它们都是继承 BasePlugin:
public abstract class BasePlugin implements Plugin{
@Override
public final void apply(@NonNull Project project) {
//核心方法
basePluginApply(project);
//因为 apply 是 final
//所以提供一个 pluginSpecificApply 用于 AppPlugin 和 LibraryPlugin 加入特殊逻辑
pluginSpecificApply(project);
}
}
看到重写的 apply 方法是 final,就很开心了,也就是说我们只需要分析 BasePlugin 里面的逻辑就好了。核心方法是 basePluginApply,源码如下:
private void basePluginApply(@NonNull Project project) {
//核心三个方法
configureProject();
configureExtension();
createTasks();
}
configureProject()
private void configureProject() {
//主构建器类,它提供了处理构建的所有数据,比如 DefaultProductFlavor、DefaultBuildType 以及一些依赖项,在执行的时候使用它们特定的构建步骤
AndroidBuilder androidBuilder = new AndroidBuilder();
//...
// 依赖 Java 插件
project.getPlugins().apply(JavaBasePlugin.class);
//Plugin 的全局变量
new GlobalScope()
}
configureExtension()
private void configureExtension() {
ObjectFactory objectFactory = project.getObjects();
//创建 BuildType、ProductFlavor、SigningConfig、BaseVariantOutput 的容器
final NamedDomainObjectContainer buildTypeContainer =
project.container());
final NamedDomainObjectContainer productFlavorContainer =
project.container());
final NamedDomainObjectContainer signingConfigContainer =
project.container());
final NamedDomainObjectContainer buildOutputs =
project.container(BaseVariantOutput.class);
project.getExtensions().add("buildOutputs", buildOutputs);
sourceSetManager = new SourceSetManager();
//创建 BaseExtension
extension = createExtension();
globalScope.setExtension(extension);
variantFactory = createVariantFactory(globalScope, extension);
//创建 TaskManager
taskManager = createTaskManager();
//创建 VariantManager
variantManager = new VariantManager();
registerModels(registry, globalScope, variantManager, extension, extraModelInfo);
variantFactory.createDefaultComponents(
buildTypeContainer, productFlavorContainer, signingConfigContainer);
}
createTask()
private void createTasks() {
//注册一些默认 Task,比如 uninstallAll、deviceCheck、connectedCheck 等
taskManager.createTasksBeforeEvaluate()
createAndroidTasks();
}
final void createAndroidTasks() {
//...
List variantScopes = variantManager.createAndroidTasks();
}
然后就一直到了 TaskManager#createTasksForVariantScope 方法,它是一个抽象方法,我们看它在 ApplicationTaskManager 中的实现:
public void createTasksForVariantScope(@NonNull final VariantScope variantScope) {
createAnchorTasks(variantScope);
//checkXxxManifest 检查 Manifest 文件存在、路径
createCheckManifestTask(variantScope);
handleMicroApp(variantScope);
//把依赖放到 TransformManager 中
createDependencyStreams(variantScope);
// Add a task to publish the applicationId.
createApplicationIdWriterTask(variantScope);
// 合并 manifest
createMergeApkManifestsTask(variantScope);
// Add a task to create the res values
createGenerateResValuesTask(variantScope);
// Add a task to compile renderscript files.
createRenderscriptTask(variantScope);
// 合并 resource
createMergeResourcesTask(
variantScope,
true,
Sets.immutableEnumSet(MergeResources.Flag.PROCESS_VECTOR_DRAWABLES));
// 合并 assets 文件夹
createMergeAssetsTask(variantScope);
// 生成 BuildConfig.java 文件
createBuildConfigTask(variantScope);
// Add a task to process the Android Resources and generate source files
createApkProcessResTask(variantScope);
// Add a task to process the java resources
createProcessJavaResTask(variantScope);
createAidlTask(variantScope);
// Add external native build tasks
createExternalNativeBuildJsonGenerators(variantScope);
createExternalNativeBuildTasks(variantScope);
// Add a task to merge the jni libs folders
createMergeJniLibFoldersTasks(variantScope);
// Add feature related tasks if necessary
if (variantScope.getType().isBaseModule()) {
// Base feature specific tasks.
taskFactory.register(new FeatureSetMetadataWriterTask.CreationAction(variantScope));
createValidateSigningTask(variantScope);
// Add a task to produce the signing config file.
taskFactory.register(new SigningConfigWriterTask.CreationAction(variantScope));
if (extension.getDataBinding().isEnabled()) {
// Create a task that will package the manifest ids(the R file packages) of all
// features into a file. This file's path is passed into the Data Binding annotation
// processor which uses it to known about all available features.
//
// see: {@link TaskManager#setDataBindingAnnotationProcessorParams(VariantScope)}
taskFactory.register(
new DataBindingExportFeatureApplicationIdsTask.CreationAction(
variantScope));
}
} else {
// Non-base feature specific task.
// Task will produce artifacts consumed by the base feature
taskFactory.register(
new FeatureSplitDeclarationWriterTask.CreationAction(variantScope));
if (extension.getDataBinding().isEnabled()) {
// Create a task that will package necessary information about the feature into a
// file which is passed into the Data Binding annotation processor.
taskFactory.register(
new DataBindingExportFeatureInfoTask.CreationAction(variantScope));
}
taskFactory.register(new MergeConsumerProguardFilesTask.CreationAction(variantScope));
}
// Add data binding tasks if enabled
createDataBindingTasksIfNecessary(variantScope, MergeType.MERGE);
// Add a compile task
createCompileTask(variantScope);
createStripNativeLibraryTask(taskFactory, variantScope);
if (variantScope.getVariantData().getMultiOutputPolicy().equals(MultiOutputPolicy.SPLITS)) {
if (extension.getBuildToolsRevision().getMajor() < 21) {
throw new RuntimeException(
"Pure splits can only be used with buildtools 21 and later");
}
createSplitTasks(variantScope);
}
TaskProvider buildInfoWriterTask =
createInstantRunPackagingTasks(variantScope);
createPackagingTask(variantScope, buildInfoWriterTask);
// Create the lint tasks, if enabled
createLintTasks(variantScope);
taskFactory.register(new FeatureSplitTransitiveDepsWriterTask.CreationAction(variantScope));
createDynamicBundleTask(variantScope);
}
从上面的流程图,我可以看出 APK 构建流程可以分为以下七步:
aapt(Android Asset Packaging Tool),用于打包资源文件,生成 R.java 和编译后的资源。aapt 的源码位于 frameworks/base/tools/aapt2 目录下。
用于处理 aidl 文件,源码位于 frameworks/base/tools/aidl。输入 aidl 后缀的文件,输出可用于进程通信的 C/S 端 Java 代码,位于 build/generated/source/aidl。
有了 R.java 和 aidl 生成的 Java 文件,再加上工程的源代码,现在就可以使用 javac 进行正常的 java 编译生成 class 文件了。
输入:java source 的文件夹(另外还包括了 build/generated 下的:R.java,aidl 生成的 Java 文件以及 BuildConfig.java)
输出:对于 gradle 编译,可以在 build/intermediates/classes 里,看到输出的 class 文件。
使用 dx/d8 工具将 class 文件转化为 dex 文件,生成常量池,消除冗余数据等。
打包生成 APK 文件,现在都已经通过 sdklib.jar 的 ApkBuilder 类进行打包了,输入为我们之前生成的包含 resources.arsc 的 .ap_ 文件,上一步生成的 dex 文件,以及其他资源如 jni、jar 包内的资源。
对 apk 文件进行签名,APK 需要签名才能在设备上进行安装。现在不仅可以使用 jarsigner,还可以使用 apksigner。
对签名后的 apk 文件进行对齐处理,使得 apk 中所有资源文件距离文件起始偏移为四个字节的整数倍,从而在通过内存映射访问 apk 文件时会更快。
需要注意的,使用不同的签名工具对对齐是有影响的。如果使用 jarsigner,则只能在 APK 文件签名后执行 zipalign;如果使用 apksigner,则只能在 APK 文件签名之前执行 zipalign
来源:https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6MzVmYmFlN2FhYjkzMzc2Ng
这里分析的是AS3.4.0版本的源码,整个AS源码编译相当复杂,耗时非常久,后期我会出专门的编译AS的教程
AndroidRunConfigurationBase-->getState
核心方法是doGetState(),然后我们接下来看下这个方法的源码,由于这个方法比较大,我们只看其中一部分
在这里选择设备进行部署安装
IDEA 允许你在执行之前,执行一些任务,比如一个 Java 项目在运行之前,你得编译。我们的 Android 项目也是类似,在安装和部署之前,你得编译打包。这个过程称之为:Before Launch。
在编译app的时候可以配置
AS 默认为我们提供 Gradle-aware Make 。其中Before launch对应的源码为:BeforeRunBuilder
public interface BeforeRunBuilder {
boolean build(@NotNull GradleTaskRunner taskRunner, @NotNull List commandLineArguments) throws InterruptedException,
InvocationTargetException;
}
如下面代码,本质上去执行了 Gradle Tasks,在 Debug 环境下默认是assembleDebug , 如果用户更改了 Build Variants 也会相应变化,如下图
可以看到在createBuilder()方法里面
DefaultGradleBuilder这里就是组装gradle编译命令,比如:
组装 Gradle task:gradle[:"assemble" + compileType]
最终会通过反射的方法调用到GradleBuildInvoker,这个对象主要就是直接调用gradle的方法,然后把执行的过程显示在AS面板的Message里面,同时在Gradle console里面显示,这个就是AS和Gradle真正结合的地方,接下来我们看下这个对象的具体核心方法
在构建完成之后,会回到 RunState 的执行阶段,这一阶段应该叫做部署,核心代码在
AndroidRunState--execute()方法
此时会调用到LaunchTaskRunner这个对象,由于这是一个Runable,那么接下来会调用它的run方法
通过分析源码可以看到,InstantRun 相关逻辑,版本判断,设备判断,输出日志,调用 pm 命令安装 APK,唤起首屏等等
五 总结
通过上面的分析,相信大家已经清楚了AS在编译apk的整个流程,可能细节上还有些不是很清楚,那么大家可以自行阅读代码,我在这里就是抛砖引玉,要真正的知道AS编译apk的流程,需要清楚的知道AS编译apk依赖的RunStats对象,这个是AS在编译的时候很重要的一个流程控制状态机,同时还需要搞明白AS和gradle在那个阶段结合的,在它们两者结合完毕之后,剩余的工作交给gradle,在gradle做完自己的事情完毕之后,又会通知AS,完整最后的安装,部署。