zoukankan      html  css  js  c++  java
  • Gradle的奇妙之处

    转载请注明出处: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的基本配置

    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到此结束~
    感谢大家支持!

    如有错误,请指出~
    谢谢~

  • 相关阅读:
    利用存储过程生成随机数,并用其它存储过程调用此过程
    dataGridView中的数据操作
    listView绑定数据
    我的简易 数据存取器 的封装
    dataGridView 控件的简单绑定
    文件写入写出的封装
    Button1.Attributes.Add()方法小结
    自定义分页栏
    安迪的找工作日志——9.12笔试(优酷土豆)问题及解答
    安迪的找工作日志——9.13在教四四楼墙上看到的
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/7082265.html
Copyright © 2011-2022 走看看