心想將近一年沒升級ADT,也該升級一下了。
上google官方的開發者首頁之後才想起來google在去年底推出Android Studio。
(這個時間點是1.1版)於是下載來更新。 應該會跟之前Eclipse改到ADT那樣的無痛吧?
但是把Project匯入之後,才發現事情完全不是如想像的那樣...
有一大堆東西要重新設定。好不容易整理完(光是這個整理的流程就又可以寫一篇)之後,視窗一直跳出提醒:
...Gradle? What!?
點了「More information...」出現了Gradle的user guide:
http://tools.android.com/tech-docs/new-build-system/user-guide
看了一下(英文不好,花了快近8小時看完),似乎是為了解決之前的舊系統要讓同一份code可以產生不同版本(免費跟付費版)可以比較方便。對於正在用同一份程式碼開發多版本的人來說還蠻方便的。
決定花一些時間來了解,順便轉移程式碼到此架構。
就算是失敗了也還有之前已經轉移到Android studio的基礎可以繼續用。
花了時間啃完硬到不行的user guide,還是一知半解。
還是從空的project來了解最快。
總之開了一個空的計畫:
從檔案總管看到的檔案結構:
文件裡面提到,所有的操作描述全部都放在build.gradle這個檔案裡面。
總之打開project根目錄的build.gradle瞧瞧。
...哇靠,這是啥?一些文件裡面沒提到的指令,看不懂。
有個檔案:settings.gradle也蠻可疑的。總之打開來看看:
include ':app'
看來這檔案是拿來include 「app」用的。
這時又想起文件內的一句話:project設定的操作都可以使用Android studio的「Project structure」來做修改。意下就是不要自己手改檔案...
好吧。繼續從Android studio裡面觀察。
發現還有另一個build.gradle:「Module: app」裡面的。
旁邊寫著「Module:app」,所以「app」就是指Module嘍。
總之打開來看看:
apply plugin: 'com.android.application' android { compileSdkVersion 19 buildToolsVersion "21.1.1" defaultConfig { applicationId "com.akazukin.chacha" minSdkVersion 15 targetSdkVersion 19 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) }
總算有不少文件裡面有提到的指令了。
先來找找要怎麼增加Module吧? 總之在Module「app」上面按右鍵:
喔,有指令可用。
總之加了一個Module 「chachalibrary」:
看來「Module」比較像舊系統的的「Project」。有自己的res/manifest。
那要怎麼設定Module 的關聯,讓Module可以include進來?
打開「Project Structure」:
選擇「Module depedency」:
然後會自動列出可以import的模組:
就加好了。也太簡單XD
順道一提,「Scope」的「Complie」是指這個project在make project的階段會被compile。provided的話聽說還沒有完整實作,建議不要用。
這時再回來看「app」的build.gradle,發現多了一行
「compile project(':chachalibrary')」
apply plugin: 'com.android.application' android { compileSdkVersion 19 buildToolsVersion "21.1.1" defaultConfig { applicationId "com.akazukin.chacha" minSdkVersion 15 targetSdkVersion 19 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':chachalibrary') }
看來加模組的方式就是如法泡製,加在depedencies裡面就好。
假如要import的是已經編成jar的project的話,請直接copy jar檔擺在
app/libs 目錄裡面再加在depedencies裡面。
直接使用import project的方法,可能主程式碼會無法參照內部的class跟function。
import eclipse project成module的流程:
- android studio的視窗menu->File->Import Module,選擇要import的模組(會自動copy所需檔案,不需要先copy進project裡面),一步步照系統給你的流程做完。
- 修改project根目錄的settings.gradle,加上
「include ':< module名稱>'」 - 重啓android studio(因為不會refresh...)
- 修改project root/< module名稱>/build.gradle(這個時候會因為buildToolsVersion指定的19版沒有安裝而沒出現在project裡面,請直接使用文字編輯程式編輯),調整compileSdkVersion/buildToolsVersion兩個參數,讓它跟app Module裡面的build.gradle參數相同。dependencies若是沒有「compile fileTree(dir: 'libs', include: ['*.jar'])」這一行的話就加上去。
- 一樣在build.gradle裡面,defaultConfig至少要加這兩個參數,要不然可能無法被其他模組參照:
{
minSdkVersion 15
targetSdkVersion 15
} - AndroidManifest.xml若是有參數衝突的話,拿掉import進來的module裡面的AndroidManifest.xml的參數。
- 在lib路徑有擺android support library的話,請刪除,改用系統提供的。(在project structure的dependencies裡面可以add library depedency,有android support library可以選。記住,每個有include的都要選一樣的版本,否則之後執行的時候會出「multiple dex files define landroid/support/annotation/AnimRes」的錯誤。)
注意這幾點應該就可以compile了。
繼續測試文件裡面還有提到的三個重要觀念:
sourceSet / buildtype / flavor。
buildtype:通常是debug/release兩種。
flavor:通常是拿來設定package name,package sign,切換resources目錄的設定用。
這樣就可以達到同一份程式碼只要更換package name跟簽名,連各個java檔的package class name都可以不用換就可以上google play。
sourceSet:從檔案結構的角度來看,就是src目錄下面的「main」「androidTest」這兩個目錄。但是打開目前所有的build.gradle檔案都沒提到這幾個關鍵字。
看來得自己做了。
依照Stackoverflow的高手的建議,先宣告flavor再宣告sourceSet。
sourceSets { main { manifest.srcFile 'src/main/AndroidManifest.xml' java.srcDirs = ['src/main/java'] aidl.srcDirs = ['src/main/java'] renderscript.srcDirs = ['src/main/java'] res.srcDirs = ['src/main/res'] assets.srcDirs = ['src/main/assets'] } }
結果按下debug,發現跳出視窗警告「Error:AndroidManifest.xml doesn't exist or has incorrect root tag」... 暈了...Orz
強行點選Debug,點選「Continue Anyway」,
發現build完之後可以正常執行。看來是Android Studio的bug。看了這篇之後你就不會被嚇到了。XD
這時出現了一個疑問:那可不可以導入其他sourceSet目錄的檔案? 答案是可以的。
直接在MainActivity裡面直接宣告ApplicationTest class看看,以這時的狀態是會找不到class的。
但是把build.gradle做如下的修改:
sourceSets { main { manifest.srcFile 'src/main/AndroidManifest.xml' java.srcDirs = ['src/main/java', 'src/androidTest/java'] aidl.srcDirs = ['src/main/java'] renderscript.srcDirs = ['src/main/java'] res.srcDirs = ['src/main/res'] assets.srcDirs = ['src/main/assets'] } }
會發現剛剛找不到class的問題不再發生了。這樣看起來sourceset是可以互相導入,而且只要有同樣的目錄結構的話,class也會擺在一起,成為連集。
那接下來就只剩一個問題:假如兩個sourceSet有相同檔案存在的時候,gradle會怎麼處理?
繼續做實驗:把MainActivity.java copy到src/androidTest/java/com/akazukin/chacha目錄,馬上被抱怨Duplicate class...
看來soruceset的宣告,使用逗號加目錄的話,會成為連集。不能重複檔案。
要讓gradle自動選檔看來是行不通。
有趣的是,直接做一個strings.xml在src/flavor1/res/values裡面,執行之後發現app會使用該stings.xml裡面的資源。看來sourceset的設計會自動apply?
所以又做了很多實驗,希望可以搞清楚檔案的使用規則。
總結關於sourceSets / buildType / flavor的結論:連集。是的。就這兩個字...
應用的方式跟流程:
main裡面放的是最基本的設定跟必須的程式碼。
若是有設定flavor的需求,也就是根據不同的flavor給不同的設定的話,以下為flavor的使用方式:
- 不需要編輯build.gradle去增加sourceSet的設定區塊。照flavor的設定名稱,在src/main同一層增加同名的目錄即可。
- 要在同樣的資源檔案(AndroidManifest/string/values等等)上面增加設定的話,檔案擺放的目錄結構必須跟main一模一樣。如圖:
- AndroidManifest.xml:要是有兩個設定值同時存在,值卻不相同的時候,此檔案的處理結果會給error。
因此若是有針對不同flavor而必須給不一樣的數值的時候(例如googleplay api key),必須參數化,從strings或是values裡面取。無法參數化的設定(例如gcm的permission)就放在各個flavor裡面,main裡面不要放。 - res/values下面的資料是以resource name為單位,flavor的目錄裡面有定義的話就優先取用。例:flavor1 跟main同時有兩個相同的string name的話,會先取用flavor1所指定的值。
- 其他的res是以resource file為單位,flavor的目錄裡面有定義的話優先取用。例:flavor1 跟main同時有兩個相同的layout file/drawable file的話,會取用flavor1裡面的layout file/drawable file。
到此總算是對這個系統有初步的認識了。
最後,要在不同的flavor之間切換編輯的對象的話,android studio主視窗的左下角的邊條有一個「build variants」,點擊之後會出現一個小框框,試著操作一下就知道該怎麼切換了。
其他一些遇到的狀況:
- 若是有getMethod()等等reflect的使用需求的話,可能會需要跳過型態檢查。
請在project根目錄的build.gradle裡面加上:
allprojects {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
}
}
} - 執行時遇到com.android.dex.DexIndexOverflowException:在主project的build.gradle的defaultConfig裡面加上「multiDexEnabled true」。