Google現在最推薦的IDE是Android Studio,用起來很智能,但是也有占用很多內存,運行起來很卡的缺點,下面,我們就來談談Android Studio的優化吧。
-
安裝完成后啟動卡死
剛剛打開studio就卡在gradle building的界面再也不動了(去連接墻外的網下載),那么這個時候我們就需要把這個聯網下載操作屏蔽掉,找到studio安裝目錄,找到idea.properties文件,打開,加上下面一行配置,作用是在初次打開的時候不讓它連接谷歌進行更新。
disable.android.first.run=true
或者:使用墻外代理。
-
更改studio的VM大小
AS限制了Java虛擬機啟動的內存大小,限制了最大堆內存,當AS運行越久,內存越不足的時候,就會頻繁的觸發GC,AS就自然會卡起來了,嚴重的直接黑屏,所以,我們把對應的所需內存都配置大一些,32位的系統打開studio.exe.vmoptions文件,如果是64位的話打開studio64.exe.vmoptions,改動以下配置,根據各自配置適當調節。
-Xms512m
-Xmx4096m
-XX:MaxPermSize=2048m
-XX:ReservedCodeCacheSize=1024m
-
使用offline work和Local gradle
在setting->Build,Execution,Deployment->gradle路徑下,選擇Use local Gradle distribution,把gradle下載到本地,這樣本項目和其他項目用到的時候就不需要重新下載了。項目編譯運行成功后,如果依賴配置沒有修改,可以設置為offline work,這樣重新打開項目時,就不會重新編譯了。
image.png
-
開啟gradle的守護進程
在工程的gradle.properties文件中,添加以下配置:
# 編譯時使用守護進程
org.gradle.daemon=true
#JVM最大允許分配的堆內存,按需分配
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#使用并行編譯
org.gradle.parallel=true
org.gradle.configureondemand=true
工程盡量減少對module的直接依賴
將不需要頻繁改動的module從setting.gradle中去掉,直接引用module對應的aar文件。工程中有多個module時,會先編譯每一個module之后再編譯主工程,盡量少的module依賴肯定會加快編譯速度。盡量使用第三方庫的jar、aar文件導入自己的項目,或者將第三方庫下載到本地,然后當做一個本地模塊導入自己的項目,不要再使用gradle中的maven依賴了,具體可以參考Android Studio 編譯、同步慢的解決方法
Gradle的生命周期
下面說一下 gradle 的生命周期吧,gradle 構建一個工程主要分為三部分(完全掌握了下面這張圖,整個 gradle 的構建過程能了解個十之七八了):
初始化階段:主要是解析 setting.gradle 文件(因此有人提到減少 setting.gradle 的 module 數量,是很有道理的,但是實際操作過程限制頗多,原因最后會大致說一下);
讀取配置階段:主要是解析所有的 projects 下的 build.gradle 文件,包括 rootProject 和其他的 subprojects(子項目),檢查語法,確定 tasks 依賴以建立 task 的有向無循環圖,檢查 task 里引用的文件目錄是否存在等(這一步也進一步驗證了減少 setting.gradle 里的 module 數量可以加快編譯速度,因為減少一個 module ,需要解析的 build.gradle 文件就減少一個,第 3 步里就不會執行本屬于這個 module 的任務了,但是還是 1 里面說的問題,限制頗多);
-
執行階段:按照 2 中建立的有向無循環圖來執行每一個 task ,整個編譯過程中,這一步基本會占去 9 成以上的時間,尤其是對于 Android 項目來講,將 java 轉為 class
compileDebugJavaWithJavac/compileReleaseJavaWithJavac
和 將 class 合并成 dex
transformClassesWithDexForDebug/transformClassesWithDexForRelease
這兩步很耗時,第一步還好,第二步會耗時非常久。
明確了 gradle 的生命周期,那么就可以看到加快編譯速度的關鍵就是從第三步入手,當然,減少 setting.gradle 里的 modules 數量這一步也是必須的。下面說說我們公司的實踐吧。
項目插件化改造,每位業務上的同學只需要編譯一個模塊即可,這一點基本上從根本上解決了編譯慢的問題(對于大多數沒有插件化需求的朋友們可以看下面的一些實踐),首先 setting.gradle 里的 module 只有自己開發的模塊了,而對應的執行階段的任務也只有這一個 module 的任務了。
執行一次 gradle build ,我們就會發現,在這個過程中,其實是執行了多次打包任務的,在 buildTypes 里配置了多個編譯打包類型,默認有 debug 和 release ,我們還可以手動配置其他的類型,而且還有 productFlavor 里的多渠道,這樣就會執行多次編譯打包,而正常開發過程中,只需要打 debug 包去調試,因此使用 gradle assembleDebug 即可,等發版的時候使用其他方式去打多渠道的包(如美團的方案http://tech.meituan.com/mt-apk-packaging.html);
既然編譯主要時間都集中在 gradle 生命周期的第三步執行 task 任務里,那么我們就可以把一些無關緊要的任務給禁用掉,比如各種 Test ,各種 lint 等,剛好在 gradle 里有這樣的指令 -x lint 可以臨時禁掉 lint 任務,-x test 可以禁掉 test 任務,事實上對于一個稍微大一點的項目,lint 也是很耗時的,當然也可以通過 gradle 腳本徹底禁用 lint 和 test 任務,但是不太建議這么做,因為有時候 lint 和 test 也是挺有用的;
gradle 本身提供了一些指令參數可以加快編譯,比如 --daemon ,開啟守護進程,--parallel ,開啟并行編譯等,這個也可以在 gradle.propertites 里配置(編譯使用的 jvm 內存也可以在這里配置)。
定制 gradle 編譯流程,利用官方提供的 API 完全可以定制一個適合自己的編譯流程,可以參考一下攜程的 DynamicAPK/sub-project-build.gradle at master · CtripMobile/DynamicAPK · GitHub,里面有攜程他們自己整個完整的編譯流程,腳本本身很簡單,一共只有兩三百行代碼。
上面講到的幾點,現有環境就可以做到的大概是這樣(有一點要特別注意,如果工程里有交叉依賴,一定不要使用 --parallel 參數):
gradle assembleDebug --daemon --parallel -x lint -x test
,如果是要直接安裝到設備上的話,就把 assembleDebug 換成 installDebug ,assembleDebug 可以簡寫為 asD ,installDebug 可以簡寫為 iD 。
最后講一下,為什么減少 setting.gradle 里的 module 數量,確實可以加快編譯,但是卻限制頗多呢?
首先,我們想一下整個編譯過程,先去解析 gradle 配置,建立 tasks 依賴有向圖,然后再去執行每一個 module 的 task ,如果我們通過 maven 依賴,使用 aar 替掉了 module(單指 android library),如果我們要改這個 module 里的文件,豈不是每次都要修改上傳再下載,這其實還好,但是有一個致命的問題:不修改版本號的話,SNAPSHOT 在 IDEA 里經常會不好使。這樣就導致修改的東西會不生效,去解決這個問題是非常耗費時間的。不過有一種方式,可以一定程度上解決問題,增加下面的腳本:
project.configurations.all(new Action<Configuration>() {
@Override
void execute(Configuration files) {
files.resolutionStrategy.cacheDynamicVersionsFor(5, TimeUnit.MINUTES)
files.resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
}
})
那有人會問,插件化里,每個人開發一個模塊,對于每個模塊的維護不也是要打包上傳到 maven ,每次一有修改,哪怕是非常微小的修改,也要做一次上傳,同樣會遇到 SNAPSHOT 不好使的問題。