Android Studio源码分析系列一之点击Run背后的秘密

文章目录

前言

一、点击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 的界面。背后发生了什么?


一、点击Run背后的秘密?

1.1 Run背后的Gradle tasks

要搞清楚这个问题必现需要明白Gradle的构建流程,下面看下Gradle构建流程

Gradle生命周期分为三个阶段,分别是Initialization(初始化阶段),Configuration(配置阶段),和Execution(执行阶段),而执行阶段会执行一系列task,进行主要的构建工作

那么自然Run按钮背后的构建工作也是由一系列task组成的,那么我们有办法去查看这些task都有哪些么?Android Studio提供了强大的日志记录功能,主要需要进行以下3步工作:

  1. 1.1 点击View > Tool Windows > Build,将在Android Studio界面下方显示Build过程;     Android Studio源码分析系列一之点击Run背后的秘密_第1张图片

通过上图可以看到点击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 展开后

Android Studio源码分析系列一之点击Run背后的秘密_第2张图片

此时如果我们点击Run按钮以后,我们看下会做什么

Android Studio源码分析系列一之点击Run背后的秘密_第3张图片

 ,通过观察我们发现就是把之前的build流程走一遍,然后增加了install,也就是说把编译好的Apk, 安装到用户手机上面了

1.2 Tasks名称

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大概可分为五个阶段:

  1. 准备依赖包(Preparation of dependecies):在这个阶段gradle检测module依赖的所有library是否就绪。如果这个module依赖于另一个module,则另一个module也要被编译;
  2. 合并资源并处理清单(Merging resources and processing Manifest):打包资源和 Manifest 文件;
  3. 编译(Compiling):处理编译器的注解,源码被编译成字节码;
  4. 后期处理(transform: 所有带 “transform”前缀的task都是这个阶段进行处理的;
  5. 包装和出版(Packaging and publishing):library生成.aar文件,application生成.apk文件

1.3 部分Tasks详细说明

  1. mergeDebugResources解压所有的 aar 包,并且把所有的资源文件合并相关目录里;
  2. processDebugManifest把所有 aar 包里的AndroidManifest.xml中的节点,合并到项目的AndroidManifest.xml中
  3. processDebugResources

a. 调用 aapt 生成项目和所有 aar 依赖的R.java b. 生成资源索引文件 c. 输出符号表

  1. compileDebugJavaWithJavac用来把 java 文件编译成 class 文件

二、Android Gradle Plugin和AS之间的关系

1.Android Gradle Plugin 核心源码分析

有了以上的初步了解之后,再来看看 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); }


三、全面的Android构建流程

3.1 新版本的Android构建流程

Android Studio源码分析系列一之点击Run背后的秘密_第4张图片

3.2 旧版本的Android构建流程

Android Studio源码分析系列一之点击Run背后的秘密_第5张图片

从上面的流程图,我可以看出 APK 构建流程可以分为以下七步:

  1. 通过 aapt 打包 res 资源文件,生成 R.java、resources.arsc 和 res 文件(二进制和非二进制如 res/raw 和 pic 保持原样)
  2. 处理 .aidl 文件,生成对应的 Java 接口文件
  3. 通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,生成 .class 文件
  4. 通过 dx/d8 工具,将 .class 文件和第三方库中的 .class 文件处理生成 classes.dex
  5. 通过 apkbuilder 工具,将 aapt 生成的 resources.arsc 和 res 文件、assets 文件和 classes.dex 一起打包生成 apk
  6. 通过 Jarsigner 工具,对上面的 apk 进行 debug 或 release 签名
  7. 通过 zipalign 工具,对签名后的 apk 进行对齐处理
  8. 通过上面的流程再次验证了,如果我们想提升Apk构建速度那么就使用AAR包或者已经打包好的jar,避免二次编译,从而减少编译耗时,这是一个优化点

3.3:aapt

aapt(Android Asset Packaging Tool),用于打包资源文件,生成 R.java 和编译后的资源。aapt 的源码位于 frameworks/base/tools/aapt2 目录下。

3.4:aidl

用于处理 aidl 文件,源码位于 frameworks/base/tools/aidl。输入 aidl 后缀的文件,输出可用于进程通信的 C/S 端 Java 代码,位于 build/generated/source/aidl。

3.5:Java 源码编译

有了 R.java 和 aidl 生成的 Java 文件,再加上工程的源代码,现在就可以使用 javac 进行正常的 java 编译生成 class 文件了。

输入:java source 的文件夹(另外还包括了 build/generated 下的:R.java,aidl 生成的 Java 文件以及 BuildConfig.java)

输出:对于 gradle 编译,可以在 build/intermediates/classes 里,看到输出的 class 文件。

3.4:dex

使用 dx/d8 工具将 class 文件转化为 dex 文件,生成常量池,消除冗余数据等。

3.6:apkbuilder

打包生成 APK 文件,现在都已经通过 sdklib.jar 的 ApkBuilder 类进行打包了,输入为我们之前生成的包含 resources.arsc 的 .ap_ 文件,上一步生成的 dex 文件,以及其他资源如 jni、jar 包内的资源。

3.7:Jarsigner

对 apk 文件进行签名,APK 需要签名才能在设备上进行安装。现在不仅可以使用 jarsigner,还可以使用 apksigner。

3.8:zipalign

对签名后的 apk 文件进行对齐处理,使得 apk 中所有资源文件距离文件起始偏移为四个字节的整数倍,从而在通过内存映射访问 apk 文件时会更快。

需要注意的,使用不同的签名工具对对齐是有影响的。如果使用 jarsigner,则只能在 APK 文件签名后执行 zipalign;如果使用 apksigner,则只能在 APK 文件签名之前执行 zipalign

3.9 APK 构建更详细流程

来源:https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6MzVmYmFlN2FhYjkzMzc2Ng

Android Studio源码分析系列一之点击Run背后的秘密_第6张图片

四 AS源码分析

 4.1 核心代码

这里分析的是AS3.4.0版本的源码,整个AS源码编译相当复杂,耗时非常久,后期我会出专门的编译AS的教程

Android Studio源码分析系列一之点击Run背后的秘密_第7张图片

4.2 检查项目和读取基本配置

AndroidRunConfigurationBase-->getState

 Android Studio源码分析系列一之点击Run背后的秘密_第8张图片

核心方法是doGetState(),然后我们接下来看下这个方法的源码,由于这个方法比较大,我们只看其中一部分

Android Studio源码分析系列一之点击Run背后的秘密_第9张图片

在这里选择设备进行部署安装

4.3 RunStats

IDEA 允许你在执行之前,执行一些任务,比如一个 Java 项目在运行之前,你得编译。我们的 Android 项目也是类似,在安装和部署之前,你得编译打包。这个过程称之为:Before Launch。

Android Studio源码分析系列一之点击Run背后的秘密_第10张图片

 在编译app的时候可以配置

Android Studio源码分析系列一之点击Run背后的秘密_第11张图片

 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 也会相应变化,如下图

Android Studio源码分析系列一之点击Run背后的秘密_第12张图片

4.4 执行编译之前的准备工作

可以看到在createBuilder()方法里面

Android Studio源码分析系列一之点击Run背后的秘密_第13张图片 DefaultGradleBuilder这里就是组装gradle编译命令,比如:

组装 Gradle task:gradle[:"assemble" + compileType]

 最终会通过反射的方法调用到GradleBuildInvoker,这个对象主要就是直接调用gradle的方法,然后把执行的过程显示在AS面板的Message里面,同时在Gradle console里面显示,这个就是AS和Gradle真正结合的地方,接下来我们看下这个对象的具体核心方法

Android Studio源码分析系列一之点击Run背后的秘密_第14张图片

4.5 安装部署

在构建完成之后,会回到 RunState 的执行阶段,这一阶段应该叫做部署,核心代码在

AndroidRunState--execute()方法

Android Studio源码分析系列一之点击Run背后的秘密_第15张图片

此时会调用到LaunchTaskRunner这个对象,由于这是一个Runable,那么接下来会调用它的run方法

Android Studio源码分析系列一之点击Run背后的秘密_第16张图片

通过分析源码可以看到,InstantRun 相关逻辑,版本判断,设备判断,输出日志,调用 pm 命令安装 APK,唤起首屏等等

五 总结

 通过上面的分析,相信大家已经清楚了AS在编译apk的整个流程,可能细节上还有些不是很清楚,那么大家可以自行阅读代码,我在这里就是抛砖引玉,要真正的知道AS编译apk的流程,需要清楚的知道AS编译apk依赖的RunStats对象,这个是AS在编译的时候很重要的一个流程控制状态机,同时还需要搞明白AS和gradle在那个阶段结合的,在它们两者结合完毕之后,剩余的工作交给gradle,在gradle做完自己的事情完毕之后,又会通知AS,完整最后的安装,部署。

你可能感兴趣的:(android,android,studio,gradle)