Android Studio 'Run' 按钮后面的秘密

android_studio_btn_run.PNG

'Run' 按钮,一点下,Android Studio 就会开动,代码奇迹般地变成 APK,被安装到手机上,显示 APP 的界面。背后发生了什么?

点击 Run 按钮依次执行了 3 部分内容

  1. 检查项目和读取基本配置
  2. Gradle Build
  3. APK Install & Launch Activity

让我们继续,看看这个结论是怎么来的,以及一路上还发现了什么。

Android Studio 留下的面包屑

Android Studio 给我们留下了什么——日志

  • 点击Android Studio 右下角 'Event Log'


    android_studio_tab_event_log.PNG
20:55   Executing tasks: [:app:assembleDebug]

20:55   Gradle build finished in 16s 500ms
  • 点击Android Studio 左下角的 '4:Run'
    android_studio_tab_run.PNG
06/25 20:55:44: Launching app
$ adb push F:\workspace\AndroidBuildProcess\app\build\outputs\apk\debug\app-debug.apk /data/local/tmp/com.example.buildprocess.androidbuildprocess
$ adb shell pm install -t -r "/data/local/tmp/com.example.buildprocess.androidbuildprocess"
    pkg: /data/local/tmp/com.example.buildprocess.androidbuildprocess
Success


$ adb shell am start -n "com.example.buildprocess.androidbuildprocess/com.example.buildprocess.androidbuildprocess.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER

根据日志,可见:

  1. 执行了 Gradle task:assembleDebug
  2. 安装 apk, 启动 MainActivity

assembleDebug/assembleRelease

执行的是 assembleRelease 还是 assembleDebug 实际是由 build variants 设置的类型决定的。

build_variants.PNG

如果 Build Variants 修改为 Release ,点击 RUN 按钮后执行的就是 assembleRelease.

下面我们把 Build Variants 修改为 Release,点击 'RUN' 按钮,点击底部的
android_studio_tab_build.PNG

,可以看到执行的 tasks 如下:

completed successfully  51s 886ms
Starting Gradle Daemon  2s 23ms
Run build   42s 250ms
Load build  1s 89ms
Configure build 6s 598ms
Calculate task graph    129ms
Run tasks   33s 806ms
Finalize build cache configuration  1ms
:app:preBuild   15ms
:app:preReleaseBuild    783ms
:app:compileReleaseAidl 173ms
:app:compileReleaseRenderscript 112ms
:app:checkReleaseManifest   12ms
:app:generateReleaseBuildConfig 45ms
:app:prepareLintJar 5ms
:app:mainApkListPersistenceRelease  34ms
:app:generateReleaseResValues   8ms
:app:generateReleaseResources   1ms
:app:mergeReleaseResources  7s 961ms
:app:createReleaseCompatibleScreenManifests 73ms
:app:processReleaseManifest 392ms
:app:splitsDiscoveryTaskRelease 32ms
:app:processReleaseResources    2s 134ms
:app:generateReleaseSources 1ms
:app:javaPreCompileRelease  865ms
:app:compileReleaseJavaWithJavac    2s 264ms
:app:compileReleaseNdk  
:app:compileReleaseSources  
:app:lintVitalRelease   4s 93ms
:app:mergeReleaseShaders    18ms
:app:compileReleaseShaders  11ms
:app:generateReleaseAssets  
:app:mergeReleaseAssets 56ms
:app:transformClassesWithDexBuilderForRelease   2s 292ms
:app:transformDexArchiveWithExternalLibsDexMergerForRelease 6s 124ms
:app:transformDexArchiveWithDexMergerForRelease 1s 461ms
:app:mergeReleaseJniLibFolders  94ms
:app:transformNativeLibsWithMergeJniLibsForRelease  1s 749ms
:app:processReleaseJavaRes  
:app:transformResourcesWithMergeJavaResForRelease   1s 653ms
:app:validateSigningRelease 4ms
:app:packageRelease 1s 133ms
:app:assembleRelease    1ms

为了更加详细的分析 assembleRelease 执行的内容,执行以下命令:

linux/macOS

./gradlew assembleRelease --info > output.txt

windows

gradlew.bat assembleRelease --info > output.txt

可以从 output.txt 文件看到详细的处理过程。

来自官方的说明

Android Stuido 打包流程

来源:Configure your build

以下是 Android 官网的打包流程图,先学习一下。


build-process_2x.png
  1. 编译器将应用工程下的源码、资源文件、 AIDL 文件,依赖的 Module 、 AAR 库、 JAR 包转换成 DEX 文件, 其他的转换成已编译资源。
  2. 将 DEX 文件和已编译资源合并成单个 APK 。
  3. 使用 debug 或者 release keystore 对 APK 进行签名。
  4. 在生成最终的 APK 之前,打包器会使用 zipalign 工具对 APK 进行优化,以便减少在设备上运行时使用的内存。

APK 构建流程

APK 构建概览

来源:Build System Overview

build.png

APK 构建详细流程

来源:build-workflow

Android Build Process.png

根据上图来说明一下构建的流程

图中使用的工具在哪里?

<SDK>/build-tools/<buildToolsVersion>/ 目录下

  1. aapt(Android Asset Packaging Tool) 打包资源文件,生成 R.java 和已编译资源(二进制文件)

    1. Merge
    * Merge Resources 
    * Merge Manifest
    * Merge Assets 
    
    1. aapt 工具将 Merged Resources 、Merged Manifest 、Merged Assets 处理生成 R.java 和 已编译资源
  2. aidl(Android Interface Definition Language ) 文件处理

    aidl 工具根据 aidl 文件生成 Java Classes

  3. Java源码编译

    Javac 编译 R.java 、Java 代码 、Java Classes 生成 .class 文件

  4. 代码混淆( proguard )

    使用 ProGuard 工具进行混淆

  5. 转化为 dex 文件

    dx 工具会将 .class 文件转化为 Dalvik 专用的 dex 文件

  6. APK Builder

    使用 sdklib.jar 的 ApkBuilder 类将 dex 文件、已编译资源打包生成 APK 文件。

  7. 对 APK 进行签名

    使用 apksigner.jar 对 APK 进行签名

  8. Zipalign 进行优化

    使用 zipalign 工具对 APK 进行内存对齐

Android Studio 如何执行三个步骤

来源:Run Configurations

Android Run Configuration Execution Flow

The previous section talked about the overall execution flow. In this section, we look at the specific parts implemented within the android plugin. Overall, there are 3 parts to this:

  1. User presses Run/Debug. At this point, AndroidRunConfigurationBase.getState is called, and it constructs and returns an AndroidRunState
  2. The Gradle build is performed.
  3. Once the build is complete, the actual deployment is performed by the AndroidRunState.execute method.

以上是 Android Studio 对 IDEA 扩展中代码中对 Run Configuration 流程的说明。

AS 对 IDEA 扩展的代码放在:JetBrains/android。打包进 AS 之后,代码位于:ANDROID_STUDIO/plugins/android/lib/android.jar

使用以下工具查看 android.jar :

  • jd-gui
  • Luyten :如果 jd-gui 显示 '// INTERNAL ERROR //' ,不能显示类的内容,使用此工具

检查项目和读取基本配置

源代码位置:android/android/src/com/android/tools/idea/run/AndroidRunConfigurationBase.java

public RunProfileState getState(@NotNull Executor executor, 
                                @NotNull ExecutionEnvironment env)
    throws ExecutionException
  {
    // 验证项目( Gradle Sync 情况,是否是 Android 项目等)
    validateBeforeRun(executor);
    ...
      // 弹窗选择要安装的设备
      deviceFutures = deployTarget.getDevices(deployTargetState, facet, getDeviceCount(isDebugging), isDebugging, getUniqueID());
    ...
    // InstantRun 配置
    if ((supportsInstantRun()) && (instantRunEnabled) && (existingSessionInfo != null))
    {
        ...
    }
    ...
    return new AndroidRunState(env, getName(), module, applicationIdProvider, getConsoleProvider(), deviceFutures, providerFactory, processHandler);
  }

可见:主要完成了对项目的检查,Instant Run 相关配置,选择安装设备等过程。

Gradle Build

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

gradle_aware_make.PNG

Android Studio 默认为我们提供 Gradle-aware Make 。

如下面代码,本质上去执行了 Gradle Tasks,在 Debug 环境下默认是assembleDebug , 如果用户更改了 Build Variants 也会相应变化。

源代码位置:android/android/src/com/android/tools/idea/gradle/run/MakeBeforeRunTaskProvider.java

private static BeforeRunBuilder createBuilder(@NotNull final ExecutionEnvironment env, 
                                              @NotNull final Module[] modules, 
                                              @NotNull final RunConfiguration configuration, 
                                              @Nullable final AndroidRunConfigContext runConfigContext, 
                                              @Nullable final String userGoal) {
    ...
        // 组装 Gradle task:gradle[:"assemble" + compileType]
        if (deviceFutures == null || irContext == null) {
            return new DefaultGradleBuilder(gradleTasksProvider.getTasksFor(BuildMode.ASSEMBLE, testCompileType), BuildMode.ASSEMBLE);
        }
    ...
        return new InstantRunBuilder(getLaunchedDevice(targetDevices.get(0)), irContext, runConfigContext, gradleTasksProvider);
}

源代码位置:android/android/src/com/android/tools/idea/gradle/project/build/invoker/GradleBuildInvoker.java

public void executeTasks(@NotNull final Request request) {
    ...
    // 真正执行 Gradle 命令
    final GradleTasksExecutor executor = this.myTaskExecutorFactory.create(request, this.myBuildStopper);
    final GradleTasksExecutor gradleTasksExecutor;
    final Runnable executeTasksTask = () -> {
        this.myDocumentManager.saveAllDocuments();
        gradleTasksExecutor.queue();
        return;
    };
    ...
}

APK Install & Launch Activity

源代码位置:android/android/src/com/android/tools/idea/run/AndroidRunState.java

在构建完成之后,会回到 RunState 的执行阶段,这一阶段应该叫做部署 : InstantRun 相关逻辑,版本判断,设备判断,输出日志,调用 pm 命令安装 APK,唤起首屏等等。

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容