1. 程式人生 > >Gradle的奇妙之處

Gradle的奇妙之處

進行 pil 路徑名 ets 驚奇 ini .config 路徑 sebool

轉載請註明出處:http://blog.csdn.net/crazy1235/article/details/50465885

Google I/O 2013大會上公布了AS,現在已經發展到2.0-beta版本號。相信已經大部分人做Android開發的都已經由Eclipse IDE轉為AS IDE。

隨著AS版本號的更叠,也帶來不少為為開發人員提供便利的工具。比方這篇blog所描寫敘述的:http://blog.csdn.net/crazy1235/article/details/49747141

本人從AS1.2版本號開始使用,現在也用了大半年了。現將使用過程中的一些經驗分享給大家。

      • gradle的基本配置
      • gradle的簽名配置
      • 配置buildTypes
      • 配置productFlavor
      • manifest占位符
      • 加入自己定義字段
      • 自己定義導出APK的名稱
      • Demo下載


gradle的基本配置

apply plugin: ‘com.android.application‘

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.jacksen.multichannel"
        minSdkVersion 14
targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile(‘proguard-android.txt‘), ‘proguard-rules.pro‘ } } } dependencies { compile fileTree(dir
: ‘libs‘, include: [‘*.jar‘]) testCompile ‘junit:junit:4.12‘ compile ‘com.android.support:appcompat-v7:23.1.1‘ compile ‘com.android.support:design:23.1.1‘ }

上面的就是一個項目gradle的基本配置。

apply plugin: ‘com.android.application‘

表示該module是一個app module。應用了com.android.application插件,也就是主程序。

假設是一個第三方library,則應該是app plugin: ‘com.android.library’

buildTypes { } 表示構建類型。包含release和debug兩種。

能夠在這裏面配置啟用混淆、zipAlign、簽名信息等。

dependencies { } 裏面是項目的依賴信息,包含jar包和第三方庫等信息。

ApplicationId與PackageName的差別:

此前使用eclipse進行開發的時候,應用程序的包名是由manifest文件的package屬性決定的:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xxx.xxxx"
    android:versionCode="1"
    android:versionName="1.0" >

然而在使用gradle構建的時候卻多了一個applicationId。通常gradle中的applicationId和Manifest中的package是一樣的。事實上能夠使二者一致。

官方解釋說是:applicationId是你的應用在商店的唯一標識。

而package是引用資源的路徑名,也就是R文件的包名。

上面我們說到二者能夠不一致,我們能夠通過對不同的Flavor設置不同的applicationId,從而能夠導出不同“包名”的apk。而不須要改動其它的代碼。後面會細致說明。


gradle的簽名配置

關於簽名的概念不懂得,能夠參考這篇blog:Android從零單排之簽名打包

我們通常執行項目都是使用debug的簽名。只是有些使用到第三方sdk的時候,須要用到正式版的簽名,通過打包正式簽名的方式又不好調試。只是我們能夠在gradle裏面配置正式版的簽名。

android {
    signingConfigs {
        config_release {
            keyAlias ‘releaseKey‘
            keyPassword ‘123456‘
            storePassword ‘123456‘
            storeFile file(‘key/releaseKey.jks‘)
        }

        config_debug {
            keyAlias ‘debugKey‘
            keyPassword ‘123456‘
            storePassword ‘123456‘
            storeFile file(‘key/debugKey.jks‘)
        }
    }
    ......//省略其它配置
}

這裏配置了兩個簽名。jsk都放在app以下的key目錄中。所以使用的是相對路徑。

技術分享

之前有人問我是怎麽知道keyAlias 、keyPassword這些語法的。

事實上在項目的【Project Structure】中都能夠找到。

技術分享


配置buildTypes

一般在buildTypes{ }裏面配置兩個(release和debug):

buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile(‘proguard-android.txt‘), ‘proguard-rules.pro‘
            shrinkResources true //移除無效的resource文件,必須同意ProGuard才幹生效
            zipAlignEnabled true
            buildConfigField "boolean", "APP_TYPE", "true"
            manifestPlaceholders = [APP_NAME: "@string/app_name_release"]
            signingConfig signingConfigs.config
        }
        debug {
            buildConfigField "boolean", "APP_TYPE", "false"
//            manifestPlaceholders = [APP_NAME: "@string/app_name_debug"]
            applicationIdSuffix ‘debug‘
        }
    }

這裏可配置的信息非常多,比方:能否夠是debug模式、簽名配置、混淆文件等。

技術分享


配置productFlavor

多渠道打包。關鍵就在於定義多個productFlavor。

在一個flavor裏面可配置的信息非常多。

productFlavors {
        flavor_release {
            buildConfigField "boolean", "APP_TYPE", "false"
            applicationId ‘com.jacksen.multichannel.release‘
            signingConfig signingConfigs.config_release
            minSdkVersion 9
            targetSdkVersion 15
            versionCode 2
            versionName ‘2.0.1‘
        }
        flavor_debug {
            minSdkVersion 10
            applicationId ‘com.jacksen.multichannel.debug‘
            signingConfig signingConfigs.config_debug
            versionCode 5
            versionName ‘5.0‘
        }
    }

最基本的是能夠在這裏配置applicationId,就是我們上面一開始說的打包多個不同“包名”的apk。

這樣有一個應用場景就是,我們一般通過debug版本號進行測試之後,在進行release版本號的測試。

只是同一個applicationId的apk在一個測試機上僅僅能存在一個。現在我們通過配置多個flavor相應多個applicationId。就能夠在測試機上執行測試版和正式版兩個apk了

技術分享


productFlavors{ } 與 buildTypes{ }裏面的配置是多對多的關系。


比方:

buildTypes {
    release {...}
    debug {...}
}
productFlavors {
    flavor_1 {...}
    flavor_2 {...}
}

此時的配置能夠打包出四個apk,各自是:
技術分享

我們在使用as的打包工具時,能夠選擇打包某一個Build type的某一個或多個Flavors:

技術分享

假設在gradle裏面配置了簽名信息,那麽在【Generate Signed APK】的第一步填寫的簽名信息是以在gradle裏面配置並引用的為準。

我們還能夠借助gradlew命令來打包:
比方打包flavor_1相應的release和debug版本號:

技術分享

技術分享

打包flavor_2相應的release版本號:

技術分享

實際上。我們不用再記住這些命令。

AS裏面的gradle插件就能夠實現。
在AS的右側會有【Gradle】tab頁面。打開之後。先刷新一下。

會看到例如以下圖示:

技術分享

通過雙擊右邊的命令打包不同的版本號。

除了build以下的命令,還有其它的命令。大家都能夠嘗試一下。依據名字應該就能知道什麽意思,這裏不多介紹了。


manifest占位符

在打包多個版本號的時候,會遇到改動應用名稱等需求。不同的flavor有要求不通的名稱。此時能夠在Manifest文件裏使用占位符,然後在build.gradle中替換占位符即可了。

首先定義占位符:

application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="${APP_NAME}"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".LoginActivity"
            android:label="${APP_NAME}">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

在build.gradle中替換:

buildTypes {
        release {
            manifestPlaceholders = [APP_NAME: "@string/app_name1"]
        }
        debug {
            manifestPlaceholders = [APP_NAME: "@string/app_name1"]
        }
    }
 productFlavors {
        flavor_1 {
            manifestPlaceholders = [APP_NAME: "@string/app_name1"]
        }
        flavor_2 {
            manifestPlaceholders = [APP_NAME: "@string/app_name2"]
        }
    }

假設在productFlavors和buildTypes裏面都進行了替換,那麽是以productFlavors裏面的為準

假設不區分productFlavors和buildTypes的話,也能夠在defaultConfig裏進行替換:

defaultConfig {
        manifestPlaceholders = [APP_NAME: "@string/app_name_release"]
    }

事實上defaultConfig也是productFlavors的一個子集。


加入自己定義字段

Gradle在generateSources階段為每個flavor生成兩個BuildConfig.java文件(相應在release和debug目錄下)。

BuildConfig類默認會提供一些常量字段。

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "com.jacksen.multichannel";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = "";
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";
}

盡管通過上面的操作,我們能夠同一時候打包出一個debug版本號和一個release版本號。

可是我們還須要執行的時候有不同的表現。比方。release版本號不顯示一些控件。

令人驚奇的是,我們能夠通過buildConfigField在gradle裏面自己定義一些字段。

buildConfigField "String", "APP_TYPE", "debug"

我們能夠查看buildConfigField的源代碼:

/**
     * Adds a new field to the generated BuildConfig class.
     *
     * <p>The field is generated as: <code>&lt;type&gt; &lt;name&gt; = &lt;value&gt;;</code>
     *
     * <p>This means each of these must have valid Java content. If the type is a String, then the
     * value should include quotes.
     *
     * @param type the type of the field
     * @param name the name of the field
     * @param value the value of the field
     */
    public void buildConfigField(
            @NonNull String type,
            @NonNull String name,
            @NonNull String value) {
        ClassField alreadyPresent = getBuildConfigFields().get(name);
        if (alreadyPresent != null) {
            logger.info(
                    "BuildType(${getName()}): buildConfigField ‘$name‘ value is being replaced: ${alreadyPresent.value} -> $value");
        }
        addBuildConfigField(AndroidBuilder.createClassField(type, name, value));
    }

凝視寫的非常具體。該方法就是加入一個field到BuildConfig類中。

三個參數都是不可為空的

加入完成之後,就能夠在代碼中使用了:

textView.setText("BuildConfig.APP_TYPE : " + BuildConfig.APP_TYPE);

自己定義導出APK的名稱

我直接上代碼吧。

/*applicationVariants.all {
        variant ->
            variant.outputs.each {
                output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith(‘.apk‘)) {
//                        def fileName = outputFile.name.replace(".apk", "-${defaultConfig.versionName}.apk")
                        def apkType = ""
                        if (variant.flavorName.equals("releaseFlavor")){
                            apkType = "release"
                        }else if(variant.flavorName.equals("debugFlavor")){
                            apkType = "debug"
                        }
                        def fileName = new File(output.outputFile.getParent(), "app-" + apkType + "-${variant.versionName}.apk")
//                        output.outputFile = new File(outputFile.parent, fileName)
                        output.outputFile = fileName
                    }
            }
    }*/

    applicationVariants.all {
        variant ->
            variant.outputs.each {
                output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith(‘.apk‘)) {
                        def fileName = outputFile.name.replace(".apk", "-${defaultConfig.versionCode}-${defaultConfig.versionName}.apk")
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
            }
    }

這裏有兩種方式,大家執行一下測試一下就明確了。

以上就是我對gradle的一些認識~~~


Demo下載

https://github.com/crazy1235/MultiChannel

歡迎star~~


參考:
http://tech.meituan.com/mt-apk-adaptation.html
http://www.jayfeng.com/2015/11/07/Android%E6%89%93%E5%8C%85%E7%9A%84%E9%82%A3%E4%BA%9B%E4%BA%8B/
http://www.jcodecraeer.com/a/anzhuokaifa/Android_Studio/2015/0810/3281.html


此篇blog到此結束~
感謝大家支持!

如有錯誤,請指出~
謝謝~

Gradle的奇妙之處