在 AndroidStudio 工程點擊 Run 按鈕, 實際上做了什麼操作呢?


@singwhatiwanna 大神講的很詳細,我來從執行和代碼角度分析一下:通過查看AS IDE插件的源碼 解釋具體過程。

先說結論:點擊Run按鈕其實依次執行了3部分內容

  1. 檢查項目和讀取基本配置
  2. Gradle Build
  3. Apk Install LaunchActivity

-----------

Android Studio 本質上其實還是 Intellij IDEA ,AS對IDEA擴展的代碼放在:JetBrains/android。打包進AS之後,代碼位於:ANDROID_STUDIO/plugins/android/lib/android.jar。解壓並反編譯就可以看到源碼。

點擊Run按鈕對於IDEA來說,其實是執行一個事先配置好的 [ Run/Debug Configuration ],對於Android項目來說,往往是一個名為 [ Android App ] 的 Configuration。

按照IntellIJ SDK約定,一個Configuration的執行包括倆個過程:RunState 的創建 和 執行。

檢查項目和讀取基本配置

RunState的創建過程就對應開頭所說的 過程1,完整代碼請查看:AndroidRunConfigurationBase.java

如下圖所示,基本上完成了對項目的檢查,InstantRun相關配置,包括選擇目的機器(沒錯,就是每次都會彈出來讓你選擇機器的模態對話框。。。)等過程。

Gradle Build

在RunState創建完成之後,IDEA允許你在執行之前,執行一些任務,比如一個Java項目在運行之前,你得編譯。我們的Android項目也是類似,在安裝和部署之前,你得編譯打包。這個過程稱之為:Before Launch。

Android Studio默認為我們提供 Gradle-aware Make 。這一塊的代碼在:MakeBeforeRunTaskProvider.java

如下圖,本質上去執行了Gradle Tasks,在Debug環境下默認是assembleDebug, 如果用戶更改了Build Variants也會相應變化。

(在Configuration頁面你去修改Gradle-aware Make,改成任意Gradle任務,就可以去執行自己的Task啦...)

Apk Install LaunchActivity

在構建完成之後,會回到RunState的執行階段,這一階段應該叫做部署 : InstantRun相關邏輯,版本判斷,設備判斷,輸出日誌,調用pm命令安裝APK,喚起首屏等等。相應代碼在 AndroidRunState.java。

是不是比想像中要複雜?正如 @coxier 所說,通過命令行運行之所以有時候會快一點,是因為少了前後倆個步驟。另外,與這個問題相關的是點擊同步Gradle Sync按鈕實際上做了什麼,其實只要去翻翻代碼就瞬間明白了。

最後,官方的解釋在這:

JetBrains/android


點擊 Run 按鈕,就相當於執行了一次 Gradle Task,一般來說,是assembleDebug或者assembleRelease。

首先,點擊 Run 按鈕會執行一個配置,比如下圖中的app就是一個配置。

AS中有很多配置,比如:

這裡只說Android App這類配置,相信大家看圖也是可以理解的,無非就是指定一下module和Activity的名字。

另外,app配置具體要執行的task還和Build Variants有關,如下圖所示,這會決定到底是assembleDebug還是assembleRelease,整個過程就是這個樣子。

整個過程的log如下所示,很清楚了。

17:35:33 Executing tasks: [:app:assembleDebug]
17:35:34 Gradle build finished in 858ms

09/14 17:35:34: Launching app
$ adb push /Users/didi/github/VirtualAPK/app/build/outputs/apk/app-debug.apk /data/local/tmp/com.didi.virtualapk
$ adb shell pm install -r "/data/local/tmp/com.didi.virtualapk"
Success

$ adb shell am start -n "com.didi.virtualapk/com.didi.virtualapk.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Client not ready yet..Waiting for process to come online
Connected to process 21777 on device samsung-sm_g9500-98895a473737504e42

其他類型的配置是如何Run的,我沒咋研究過,其他同學可以補充下。


無非就是幾個過程:

  1. 將代碼打包成APK,這裡面涉及到編譯、打包、簽名、混淆等;
  2. 安裝APK到設備;
  3. 在設備上運行APK。

其實就是執行一系列的gradle task,每個task需要執行的功能都已經定義好了,具體可以查看gradle console,有詳細的信息:


gradle installDebug


實際上複雜一點,比如先執行以下Gradle Sync,根據當前項目屬性和鏈接設備的類型創建一系列的構建參數,再執行構建任務,沒有什麼能比源碼更清晰的了 :MakeBeforeRunTaskProvider.java


首先會執行grade assembleDebug的task,

相當於你在命令行中輸入./gradlew assembleDebug

然後點run

$ adb push /home/shanjinwei/work/OPWeather/build/outputs/apk/debug/OPWeather-debug.apk /data/local/tmp/net.oneplus.weather
$ adb shell pm install -t -r "/data/local/tmp/net.oneplus.weather"
Success
$ adb shell am start -n "net.oneplus.weather/net.oneplus.weather.app.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER

如果你開啟了instant run ide會執行命令

adb install-multiple -r -t

大概就是這個樣子


RunMake Project 對比:

Run的時候,只編譯與要運行相關的代碼,無關的代碼不會檢查。

Make Project 編譯工作空間所有的項目。

比如,我們如果單元測試的代碼有問題,直接Run是不會檢查的。但是Make Project會。因為Run的時候僅執行了assembleDebug,但是跑單元測試時需要執行 assembleDebug和assembleDebugAndroidTest。

比如,我們Run MoudleA,MoudleA依賴MoudleB,MoudleA不依賴MoudleC,那麼Run MoudleA是不會檢查到MoudleC的代碼是否編譯通過。

Run一個Android的APP啟動詳細情況:

1、執行Gradle:(從AS的Gradle Console可以看到詳細的Task執行情況)

Executing tasks: [:app:assembleDebug]

2、Launching app:(從AS的run窗口查看詳細情況)

&>&>&>&> apk無更新的情況

No apk changes detected since last installation, skipping installation of E:xtcworkplaceIMappuildoutputsapkapp-debug.apk

(1)Kill當前應用

$ adb shell am force-stop com.xxx.demo

(2)啟動應用指定的Activity到指定的機器

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

Connected to process 5467 on device vivo-vivo_y51a-614ac269

&>&>&>&>&>&> apk有更新的情況

(1)把前面編譯好的apk包push到機器/data/local/tmp/目錄中

$ adb push E:xtcworkplaceIMappuildoutputsapkapp-debug.apk /data/local/tmp/com.xxx.demo

(2)安裝應用

$ adb shell pm install -t -r "/data/local/tmp/com.xxx.demo"

pkg: /data/local/tmp/com.xxx.demo

Success

(3)啟動應用指定的Activity到指定的機器

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

Connected to process 7364 on device vivo-vivo_y51a-614ac269

Make Project 詳細情況:

[ :moudule1:generateDebugSources,

:moudule1:generateDebugAndroidTestSources,

:moudule1:mockableAndroidJar,

:moudule1:prepareDebugUnitTestDependencies,

:moudule1:compileDebugAndroidTestSources,

:moudule1:compileDebugUnitTestSources,

:moudule1:compileDebugSources,

:app:generateDebugSources,

:app:generateDebugAndroidTestSources,

:app:mockableAndroidJar,

:app:prepareDebugUnitTestDependencies,

:app:compileDebugAndroidTestSources,

:app:compileDebugUnitTestSources,

:app:compileDebugSources]

主要執行了上述幾個task。

上述回答中,Build Variant 為 debug。


對於像我對gradle不是很熟悉的同學,可以這樣理解 debug 模式下的 run。

  1. Android Studio 提供了 GUI 圖形界面,那個 run 按鈕點擊後,會執行一個腳本
  2. 腳本運行的實質應該是執行了兩個主要任務:assembleDebug 和 installDebug

對於項目大了之後,一般都不點擊 run 按鈕了,改用腳本執行這兩個任務,因為太慢了(猜測做了一些hook)。


別用IDE,只用命令行下的vim和gradle的命令,你就知道幹了什麼了。

起碼你會自己寫build.gradle 然後 gradle build 。然後就生成了apk文件。然後adb install安裝到測試手機。然後 adb shell am start 啟動到相應的activity。然後adb logcat再看日誌。


推薦閱讀:

TAG:Android | Gradle | AndroidStudio |