使用Android Studio構建Cordova項目

如今Android開發的IDE用戶漸漸轉投到Android Studio下了。而在用Cordova開發時,雖然多數時候是web開發,但有些狀況,好比開發自定義插件時,仍是要進行Android開發。那麼就須要在Android Studio中打開Cordova項目中的Android部分。java

在我之前關於Cordova的例子中,若是遇到Android開發工做,IDE用的都是Eclipse。如今Cordova 5已經在建立項目時爲咱們生成Gradle腳本了。這樣在Android Studio打開時就能夠按現有的Android Studio項目打開。android

而關於Gradle基礎,原本想詳細的寫一下。但一位叫neu的做者的譯做《Gradle for Android》(如下簡稱G文)已經寫的很詳細了,並且同名書也是一本好書,因此推薦,再也不贅述。web

用之前一篇叫《構建一個完整的Cordova應用》的代碼爲例,打開後項目結構以下圖:apache

圖片描述

項目結構

從圖1中能夠看到導入的項目結構。能夠看到和在Android Studio新建的安裝項目的結構有些不一樣。它包括CordovaLib模塊、android模塊和一些gradle腳本。api

通常Android項目結構和目錄結構同樣,是這樣的:數組

MyApp架構

|--build.gradle
|--settings.gradle
|--app
    |-- build.gradle
    |-- build
    |-- libs
    |-- src
        |-- main
            |--java
            |   |-- com.package.myapp
            |--res
                |-- drawable
                |-- layout
                |-- etc

由Cordova建立的項目的目錄結構是這樣的(項目結構見上圖)app

圖片描述

看起來很亂是否是。可是不影響使用,實際上也不必非要改爲標準格式。由於Gradle已經幫咱們在配置腳本中寫好了相關配置,它知道如何找到須要的文件。maven

按照標準的Gradle教程,項目即便沒有任何模塊(module),Android Studio也會爲咱們生成一個對應於項目的build.gradle腳本文件的。而觀察圖1,Android Studio打開的Cordova項目中卻沒有,編譯構建工做也能夠正常進行,說明這個文件不是必須的。函數

它是在andorid、CordovaLib模塊中的build.gradle腳本中的android任務的sourceSets的main屬性,其中定義了Android Studio項目目錄結構和真實目錄的對應關係。好比android模塊下的build.gradle是這樣的:

sourceSets {
    main {
        manifest.srcFile 'AndroidManifest.xml'
        java.srcDirs = ['src']
        resources.srcDirs = ['src']
        aidl.srcDirs = ['src']
        renderscript.srcDirs = ['src']
        res.srcDirs = ['res']
        assets.srcDirs = ['assets']
        jniLibs.srcDirs = ['libs']
    }
}

詳細請參考G文第一篇 '保持舊的Eclipse文件結構' 部分。

能夠看到,Android Studio中目錄的邏輯結構和系統中的文件夾結構是分離的,依靠的就是Gradle的配置能力。對比標準的項目目錄結構,Cordova項目把android模塊直接放在了項目的根下,而不是其餘模塊那樣(好比CordovaLib模塊)做爲子文件夾存在,這也說明了它爲何不須要項目的build.gradle腳本,由於項目的腳本就是android模塊的腳本。以後添加的模塊,都是做爲根目錄的子文件夾存在的。

此外還有一個多出來的cordova.gradle,以及和G文名稱不一致的local.properties。

Gradle腳本的詳細說明

上面部分讓咱們對用Android Studio打開的Cordova項目有了些感性瞭解。它有其自已的獨特性:一方面Cordova建立的項目有本身的Gradle配置腳本,另外一方面又保留了本身獨特的結構。接下來讓咱們對Gradle腳本的各個部分做較爲詳細的瞭解,一樣的基礎知識請參考G文或《Gradle for Android》一書。

咱們看到Gradle腳本相關的文件有build.gradle(CordovaLib模塊)、build.gradle(android模塊)、cordova.gradle(CordovaLib模塊)、settings.gradle(項目)、local.properties(SDK Location)。看起來有些不同。

不過不要緊,咱們知道Gradle構建時首先要去找build.gradle腳本的。經過上面的項目結構知道,首先執行的應該是邏輯上屬於android模塊,但實際上位於項目根目錄下的build.gradle。就從這裏開始。

android模塊的build.gradle腳本

打開build.gradle。首先看到一些Cordova生成的一些註釋。原文就不照抄了,大意是一些受權說明信息。最後的單行註釋很關鍵:"生成文件!請不要編輯!"。 雖然不能編輯,但一則其餘的文件並無這麼寫,二則作爲入口文件,咱們仍是須要對它作一個瞭解。

註釋後緊接着就是buildscript方法。在它裏面首先是repositories方法,它告訴咱們使用的庫是mavenCentral。接下來是依照已經在本地安裝的gradle的版本選擇使用的Gradle插件版本,而且語句上面有註釋能夠參考,之後幾乎在每條語句上面都有註釋幫助咱們理解並告訴咱們相關參考資料的位置。

// 列表1-1。
buildscript {
    repositories {
        mavenCentral()
    }

    // Switch the Android Gradle plugin version requirement depending on the
    // installed version of Gradle. This dependency is documented at
    // http://tools.android.com/tech-docs/new-build-system/version-compatibility
    // and https://issues.apache.org/jira/browse/CB-8143
    if (gradle.gradleVersion >= "2.2") {
        dependencies {
            classpath 'com.android.tools.build:gradle:1.0.0+'
        }
    } else if (gradle.gradleVersion >= "2.1") {
        dependencies {
            classpath 'com.android.tools.build:gradle:0.14.0+'
        }
    } else {
        dependencies {
            classpath 'com.android.tools.build:gradle:0.12.0+'
        }
    }
}

隨後又使用了一個repositories任務。註釋說:容許插件經過build-extras.gradle聲明Maven依賴。經過extra這個名字,以及前面不要編輯的警告,它的做用多是對這個build.gradle文件的修改或補充。其實在下面的代碼中能夠看到更多這方面的信息。

// 列表1-2。
repositories {
    mavenCentral()
}

接下來有一個wrapper任務,查閱文檔得知它用於定製Gradle Wrapper的。若是對Wrapper不瞭解,請參考《G》文的第一篇"使用Gradle Wrapper"部分。

// 列表1-3。
task wrapper(type: Wrapper) {
    gradleVersion = '2.2.1'
}

接下來定義了一個ext屬性,其中定義了一些額外屬性。註釋也說明了其中定義的屬性須要經過環境變量、build-extras.gradle或gradle.properties設置。

ext屬性中首先引用了cordova.gradle。這樣就知道項目中這個文件用在哪裏了。但下面並無用到它。經過後面的代碼推測它多是供build-extras.gradle調用的。接下來一系列條件判斷語句分別定義了一些屬性並把它們初爲null。這些屬性是都以cdv開頭,表示一些Cordova構建屬性。接下來的代碼中會看到它們的做用。最後定義了一個cdvPluginPostBuildExtras數組變量,用來向裏面追加Gradle插件擴展。

// 列表1-4。
ext {
    apply from: 'CordovaLib/cordova.gradle'
    // The value for android.compileSdkVersion.
    if (!project.hasProperty('cdvCompileSdkVersion')) {
        cdvCompileSdkVersion = null;
    }
    // The value for android.buildToolsVersion.
    if (!project.hasProperty('cdvBuildToolsVersion')) {
        cdvBuildToolsVersion = null;
    }
    // Sets the versionCode to the given value.
    if (!project.hasProperty('cdvVersionCode')) {
        cdvVersionCode = null
    }
    // Sets the minSdkVersion to the given value.
    if (!project.hasProperty('cdvMinSdkVersion')) {
        cdvMinSdkVersion = null
    }
    // Whether to build architecture-specific APKs.
    if (!project.hasProperty('cdvBuildMultipleApks')) {
        cdvBuildMultipleApks = null
    }
    // .properties files to use for release signing.
    if (!project.hasProperty('cdvReleaseSigningPropertiesFile')) {
        cdvReleaseSigningPropertiesFile = null
    }
    // .properties files to use for debug signing.
    if (!project.hasProperty('cdvDebugSigningPropertiesFile')) {
        cdvDebugSigningPropertiesFile = null
    }
    // Set by build.js script.
    if (!project.hasProperty('cdvBuildArch')) {
        cdvBuildArch = null
    }

    // Plugin gradle extensions can append to this to have code run at the end.
    cdvPluginPostBuildExtras = []
}

接下來這部分代碼,首先判斷build-extras.gradle文件是否存在,若是存在則把它應用到構建腳本中。下面的判斷語句檢查build-extras.gradle是否認義了列表1-4的屬性。若是有就使用build-extras.gradle中的,若是沒有,則按下面語句設置:

// 列表1-5。
// Set property defaults after extension .gradle files.
if (ext.cdvCompileSdkVersion == null) {
    ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget()
}
if (ext.cdvBuildToolsVersion == null) {
    ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
}
if (ext.cdvDebugSigningPropertiesFile == null && file('debug-signing.properties').exists()) {
    ext.cdvDebugSigningPropertiesFile = 'debug-signing.properties'
}
if (ext.cdvReleaseSigningPropertiesFile == null && file('release-signing.properties').exists()) {
    ext.cdvReleaseSigningPropertiesFile = 'release-signing.properties'
}

// Cast to appropriate types.
ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean();
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : Integer.parseInt('' + cdvMinSdkVersion)
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)

上述代碼表示若是沒有設置列表1-4中的屬性時設置它們,其中調用了cordova.gradle腳本中的方法。如前所述以後會說明這些變量的做用。

接下來這段代碼,註釋告訴咱們,要讓cdvBuild的任務依賴於debug/arch-specific,即平臺相關,解決的是不一樣平臺構建的問題。首先分紅debug和release版,而後根據前面遇到過的cdvBuildMultipleApks和cdvBuildArch的變量判斷是不是跨平臺構建,若是是則返回一個根據架構名生成的結果。注意到這個cdvBuildArch變量,在上面列表1-4的註釋中標明它由build.js設置。這個腳本位於項目目錄的cordova/lib/文件夾中,沒有歸入到構建中,是cordova cli構建腳本,在這裏先不作探討。

def computeBuildTargetName(debugBuild) {
    def ret = 'assemble'
    if (cdvBuildMultipleApks && cdvBuildArch) {
        def arch = cdvBuildArch == 'arm' ? 'armv7' : cdvBuildArch
        ret += '' + arch.toUpperCase().charAt(0) + arch.substring(1);
    }
    return ret + (debugBuild ? 'Debug' : 'Release')
}

// Make cdvBuild a task that depends on the debug/arch-sepecific task.
task cdvBuildDebug
cdvBuildDebug.dependsOn {
    return computeBuildTargetName(true)
}

task cdvBuildRelease
cdvBuildRelease.dependsOn {
    return computeBuildTargetName(false)
}

接下來定義了一個任務,顯然做用是輸出列表1-4以及後面定義過的屬性值。

task cdvPrintProps << {
    println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
    println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
    println('cdvVersionCode=' + cdvVersionCode)
    println('cdvMinSdkVersion=' + cdvMinSdkVersion)
    println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
    println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
    println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
    println('cdvBuildArch=' + cdvBuildArch)
    println('computedVersionCode=' + android.defaultConfig.versionCode)
    android.productFlavors.each { flavor ->
        println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
    }
}

接下來就是構建的核心部分,每一個使用android插件構建都會有的android方法。裏面對android的構建定義了一些配置。這些配置的作用能夠參考相關文章,這裏就不贅述了。以後是在任務準備就緒後,對須要驗證的任務進行驗證。再下來定義的addSigningProps函數被android方法調用,用來在須要驗證時,爲屬性文件添加驗證配置。最後配置了構建後要執行的方法,而且添加了在自定義配置文件build-extras.gradle中配置的構建後執行方法的入口。

總結

綜上,在Cordova項目中的android文件夾中的默認兩個模塊的內容做爲容器和插件的源代碼,有其相對於普通Gradle Android特殊的目錄結構。android模塊把CordovaLib和web內容結合在一塊兒生成Crodova應用,android模塊和CordovaLib經過使用共同的配置變量,保證了包括編譯SDK版本在內的一致性,並提供了額外配置的入口。但萬變不離其宗,這一切都是創建在瞭解Gradle構建工具的基礎上的。

相關文章
相關標籤/搜索