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

212 阅读10分钟

文章目录

前言

一、点击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 点击View > Tool Windows > Build,将在Android Studio界面下方显示Build过程;
通过上图可以看到点击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, 安装到用户手机上面了

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

准备依赖包(Preparation of dependecies):在这个阶段gradle检测module依赖的所有library是否就绪。如果这个module依赖于另一个module,则另一个module也要被编译; 合并资源并处理清单(Merging resources and processing Manifest):打包资源和 Manifest 文件; 编译(Compiling):处理编译器的注解,源码被编译成字节码; 后期处理(transform): 所有带 “transform”前缀的task都是这个阶段进行处理的; 包装和出版(Packaging and publishing):library生成.aar文件,application生成.apk文件 1.3 部分Tasks详细说明 mergeDebugResources:解压所有的 aar 包,并且把所有的资源文件合并相关目录里; processDebugManifest:把所有 aar 包里的AndroidManifest.xml中的节点,合并到项目的AndroidManifest.xml中 processDebugResources: a. 调用 aapt 生成项目和所有 aar 依赖的R.java b. 生成资源索引文件 c. 输出符号表

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<BaseVariantOutput> 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<VariantScope> 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.
            //
            // <p>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> 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构建流程

3.2 旧版本的Android构建流程

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

通过 aapt 打包 res 资源文件,生成 R.java、resources.arsc 和 res 文件(二进制和非二进制如 res/raw 和 pic 保持原样) 处理 .aidl 文件,生成对应的 Java 接口文件 通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,生成 .class 文件 通过 dx/d8 工具,将 .class 文件和第三方库中的 .class 文件处理生成 classes.dex 通过 apkbuilder 工具,将 aapt 生成的 resources.arsc 和 res 文件、assets 文件和 classes.dex 一起打包生成 apk 通过 Jarsigner 工具,对上面的 apk 进行 debug 或 release 签名 通过 zipalign 工具,对签名后的 apk 进行对齐处理 通过上面的流程再次验证了,如果我们想提升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 构建更详细流程 来源:docs.google.com/viewer?a=v&…

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

4.2 检查项目和读取基本配置 AndroidRunConfigurationBase-->getState

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

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

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

4.4 执行编译之前的准备工作 可以看到在createBuilder()方法里面

DefaultGradleBuilder这里就是组装gradle编译命令,比如:

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

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

AndroidRunState--execute()方法

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

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

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