“替你”总结的Gradle配置

阅读说明

参考链接:https://developer.android.com/studio/build/index.html
本片文章的内容全部参考自上面的链接,其中有些内容是直接翻译的,有些内容是结合自己的经验总结的,可能有理解错误的地方,非常希望大家能指正出来,在交流中进步。

Gradle 编译过程

编译流程图

上图展示了一个典型的 App 编译过程,主要分为以几步:

  1. 编译器将源代码(包括依赖库)转化为 DEX 文件,编译资源文件(res 以及 assets 文件下的资源)。
  2. APK Packager 整合所有的 DEX 文件和编译过的资源文件,并且对 APK 进行签名。
  3. 签名文件必须使用 Debug 版或者 Release 版,使用 Debug Keystore 生成的 app 被用来测试和分析,使用 Release Keystore 生成的 app 可以进行发布供其他用户使用。
  4. 在生成最终的 APK 之前,APK Packager 会使用 zipalign 工具优化整个 app ,以便 app 在使用的过程中更加节省内存。

自定义编译配置

Android Studio 的 gradle 插件方便我们在以下几个方面配置我们的编译选项:

Build Types - 编译类型


编译类型,包括我们最熟悉的 release 和 debug 两种类型,我们可以根据这两种类型定义出更多的类型。配置对应的 build.gradle 文件在 moudle 下,需要添加新的或者修改 Build Type ,只需要在 android{ ... }里面操作。一个示例如下:

android {
    ...
    defaultConfig {...}
    buildTypes {
        release {
            //开启混淆
            minifyEnabled true
            //混淆规则文件
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
            //apk的后缀
            applicationIdSuffix ".debug"
        }
        
        //debug的一个扩展
        jnidebug {
            // 复制debug的属性和签名配置
            initWith debug
            applicationIdSuffix ".jnidebug"
            //开启Jni调试
            jniDebuggable true
        }
    }
}

其中 initWith 可以方便我们继承其他的配置,只需要添加需要的部分。

Product Flavors - 构建不同版本


配置 apk 的版本信息,可以为每一个版本指定不同的 applicationId 和版本名称。关于 applicationId ,可以把它也理解为包名,不过和 Manifest 文件中的包名作用不同,它是用来给应用商店和设备区分不同的 app ,而 Manifest 中的 pakage 属性用来在源代码中引用 R 类和其他类。即同一份代码 applicationId 可以让它变成不同的 app 。示例配置如下:

android {
    ...
    defaultConfig {...}
    buildTypes {...}
    productFlavors {
        demo {
            applicationId "com.example.myapp.demo"
            versionName "1.0-demo"
        }
        full {
            applicationId "com.example.myapp.full"
            versionName "1.0-full"
        }
    }
}

通过上面的配置之后,如果 buildTypes 里面配置了两个编译类型,假如是 debug 和 release ,将会产生四个 apk 文件,每一种 buildType 都会和每种 flavor 进行组合拼接,进而产生不同的变种版本(Build Variant),上面对应的四个不同的变种版本分别是:demoDebug、demoRelease、fullDebug、fullRelease。

Mutiple Manifest Files - 合并多个清单文件


配置多个 Manifest 文件。经常会在项目中依赖其他项目,这个时候就会有多个 Manifest 文件,那在编译的时候该如何处理呢?这个时候需要进行合并,而且还必须有一套相应的合并规则解决和避免合并冲突。对于不同的 Manifest 文件中同一个属性的不同值,在合并的时候还需要优先级来进行判断,用高优先级的去覆盖低优先级的。关于优先级定义如下:

  • 最高优先级:buildType 的设置
  • 次高优先级:productFlavor 的设置
  • 中等优先级:在 src/main 目录下的 Manifest 文件
  • 最低优先级:各种依赖和第三方库的设置

合并规则:概括来说是这样:

  1. 合并之前,先将每个 module 里面的 buildType 内容写到 Manifest 里面去,比如你在 buildType 里面的 minSdkVersion 和targetSdkVersion 以及 versionCode 和 VersionName 等等(此时合并后的 Manifest 文件可以在 app/intermediates/manifests/* 目录下查看)。
  2. 对于同一个属性,当高优先级和低优先级都为非默认值时,如果可以匹配,那直接合并,不能匹配,就会产生冲突(这种是针对两个不同的 module 来说),下面会专门给出例子。
  3. 不管高优先级还是低优先级,如果其中一个没有设置该属性或者设置为默认的属性值,而另外一个设置了非默认的属性值,则合并的结果就是非默认的属性值,在项目编译后,可以查看 Manifest 的合并记录,该文件目录为:app/intermediates/outputs/logs/manifest*.txt。

示例:现在给出一些例子说明上述规则,我的主 module 名为 app ,新建一个依赖的 module 叫 uisdk ,现在分别给出两个 module 的 build.gradle 文件:
app/build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.0"

    defaultConfig {
        applicationId "com.example.rth.study"
        minSdkVersion 15
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    productFlavors {
        demo {
            minSdkVersion 7
            applicationId "com.rth.app"
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:24.0.0'
    compile project(':uisdk')
}

uisdk/build.gradle

apply plugin: 'com.android.library'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.0"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 24
        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:24.0.0'
}

在 app/build.gradle 里面,defaultConfig 的 minSdkVersion 为15,但我在变种版本(productFlavors 里的 demo)里设置的 minSdkVersion 为7,最终 app 的 Manifest 的 minSdkVersion 就为7,再看 uisdk 里面的 build.gradle ,minSdkVersion 为8,就是说 app 这个 module 和 uisdk 这个 module library 在同一个属性上使用了不同的非默认值,而且 library 的 Manifest 属于最低优先级,它设置的值又比优先级比它高的值还要高,就会出错,出错信息的描述也很清晰:

Error:Execution failed for task ':app:processDemoDebugManifest'.
Manifest merger failed : uses-sdk:minSdkVersion 7 cannot be smaller than version 8 declared in library 
...
Suggestion: use tools:overrideLibrary="com.example.uisdk" to force usage

根据错误信息,我们有两种方式解决这个问题:

  1. 把 app 里面的值调高,或者把 uisdk 里面的值调低。
  2. 就像上面建议的那样,使用 overrideLibrary 这个标签。该标签的作用在名字上已经体现出来了,就是直接覆盖 library 里面的设置,现在我们在 app/src/main/Manifest 里面加上这么一句:
<uses-sdk tools:overrideLibrary="com.example.uisdk"/>

就能编译通过了,这适用于比较特殊的情况,就是在依赖库里可能要适用一些新特性,这些特性在 app 的 minSdkVersion 下不能使用,而且 app 的 minSdkVersion 已经不能更改了。

标记选择器(Marker Selectors) :选择器的功能可以让一些属性在某些 libary 里面无效,比如就拿上面的例子来说,我想让 uisdk 只处理 ui 上的东西,不想让他具有网络访问的功能,那么我可以这么设置:

<uses-permission android:name="android.permission.INTERNET"
    tools:node="remove"
    tools:selector="com.example.uisdk"
    />

其中 tools:node 标签表示删除该权限,tools:selector 标签选择在哪个依赖库里执行 tools:node 表示的动作。

可以看出这些配置还是挺灵活的。

Configure dependencies - 配置依赖


这个应该是最熟悉的了,项目中经常要依赖第三方库,一个典型了例子如下:


android {...}
...
dependencies {
    //将本地 module library 编译到项目中
    compile project(":mylibrary")
    //编译远程依赖
    compile 'com.android.support:appcompat-v7:23.4.0'
    //编译本地 jar 包
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

上面主要用到的方式是 compile ,gradle 支持6种编译方式:

  • compile:对所有 buildType 以及 flavors 进行编译并打包到 apk 。
  • provided:和 compile 相似,但只在编译时使用,几只参与编译,不打包到最终 apk 。
  • apk:只会打包到 apk 中,不参与编译,所以不能在项目代码中使用相应库中的方法。
  • test compile:相比于 compile ,仅仅针对单元测试的代码编译打包。
  • debug compile:仅针对 debug 模式编译打包。
  • release compile:仅针对 release 模式编译打包。

另外在进行 sdk 开发时,一般为了减小 sdk 体积,一些依赖库会用 provided 的方式,同时需要注意的是,对于远程依赖,compile 和 provided 的效果一样,都不会打包到 jar 包或者 arr 包中,但对于本地的 jar 包或者 arr 包的依赖,compile 和 provided 就有区别了。

Configure Sigining - 配置签名


在用 gradle 配置 release 版本的签名信息时,需要下面三个步骤:

  1. 生成一个 keystore ,一个二进制文件保存一些私钥,这个必须好好保存。
  2. 生成一个私钥,用于开发者或者公司与这个 app 建立对应关系。
  3. 将生成的信息配置到 moudle 层的 build.gradle 里。
    示例如下:
android {
    ...
    defaultConfig {...}
    signingConfigs {
        release {
            storeFile file("myreleasekey.keystore")
            storePassword "password"
            keyAlias "MyReleaseKey"
            keyPassword "password"
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
}

上面的配置中直接显示了一些敏感信息,比如各种密码,一种更加安全的方式是通过环境变量的方式获取:

storePassword System.getenv("KSTOREWD");
keyPassword System.getenv("KEYPWD");

或者如果使用命令行的方式编译,还可以让用户在命令行输入密码:

storePassword System.console().readLine("\nKeystore password: ")
keyPassword System.console().readLine("\nKey password: ")

暂时就总结到这么多了,再次说明,如果发现理解错的地方欢迎指正!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 1.介绍 如果你正在查阅build.gradle文件的所有可选项,请点击这里进行查阅:DSL参考 1.1新构建系统...
    Chuckiefan阅读 12,115评论 8 72
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,448评论 25 707
  • 转载注明出处:http://www.jianshu.com/p/5255b100930e 0. 前言 完全由个人翻...
    王三的猫阿德阅读 2,501评论 0 4
  • 记忆中的父亲用“吝啬”“懒”形容不为过,在上小学的年代,我问父亲要一毛钱买8分钱的写字薄,剩下的2分钱必须上交,我...
    绿洲家园阅读 197评论 0 3