Gradle实战

作者: 慕涵盛华 | 来源:发表于2018-07-18 11:06 被阅读17次

    前置文章 :Gradle学习总结

    通过下面几个案例来演示下Gradle的强大之处,因为在一般的开发中我们只是简单的配置下build.gradle文件(添加依赖),并不能体现它的强大,也不知道它还有那些用途。

    目录

    案例一:单独定义一个Gradle文件,存放应用中的所有配置变量和依赖,以便统一管理

    common.gradle
    //用来存放应用中的所有配置变量,统一管理,而不再是每个moudle里都自己写一份,修改起来更加的方便
    ext {//扩展Project的变量
    
      android = [compileSdkVersion   : 25,
                 buildToolsVersion   : '25.0.0',
                 applicationId       : 'com.gradle.demo',
                 minSdkVersion       : 16,
                 targetSdkVersion    : 23,
                 versionCode         : 1,
                 versionName         : '1.0.0',
                 multiDexEnabled     : true,
                 manifestPlaceholders: [UMENG_CHANNEL_VALUE: 'study']]
    
      signConfigs = ['storeFile'    : 'sign.jks',
                     'storePassword': '123456',
                     'keyAlias'     : 'alias',
                     'keyPassword'  : '123456']
    
      java = ['javaVersion': JavaVersion.VERSION_1_7]
    
    
      dependence = ['libSupportV7'           : 'com.android.support:appcompat-v7:25.0.0',
                    'libSupportMultidex'     : 'com.android.support:multidex:1.0.1',
                    'libPullAlive'           : ':lib_pullalive',
                    'libCircleImageView'     : 'de.hdodenhof:circleimageview:2.1.0',
                    'libSystembarTint'       : 'com.readystatesoftware.systembartint:systembartint:1.0.3',
                    'libUmengAnalytics'      : 'com.umeng.analytics:analytics:latest.integration',
                    'libUniversalImageLoader': 'com.nostra13.universalimageloader:universal-image-loader:1.9.5',
                    'libOkhttp'              : 'com.squareup.okhttp3:okhttp:3.3.0',
                    'libAutoScrollViewPager' : 'cn.trinea.android.view.autoscrollviewpager:android-auto-scroll-view-pager:1.1.2',
                    'libSlidableActivity'    : 'com.r0adkll:slidableactivity:2.0.5',
                    'libAndfix'              : 'com.alipay.euler:andfix:0.5.0@aar',
                    'libLogger'              : 'com.orhanobut:logger:+',
                    'libTinker'              : "com.tencent.tinker:tinker-android-lib:1.7.7",
                    'libTinkerAndroid'       : "com.tencent.tinker:tinker-android-anno:1.7.7"]
    }
    
    定义好文件后我们在根工程中引入该文件
    //引入文件
    apply from: this.file('common.gradle')
    buildscript {
        //配置工程的仓库地址
        repositories {
            jcenter()
        }
        //配置工程的"插件"依赖地址
        dependencies {
            classpath "com.android.tools.build:gradle:2.2.2"
        }
    }
    
    引入文件后,我们在app工程build.gradle文件中使用
    apply plugin: 'com.android.application'
    android {
        compileSdkVersion rootProject.ext.android.compileSdkVersion
        buildToolsVersion rootProject.ext.android.buildToolsVersion
    
        defaultConfig {
            applicationId rootProject.ext.android.applicationId
            minSdkVersion rootProject.ext.android.minSdkVersion
            targetSdkVersion rootProject.ext.android.targetSdkVersion
            versionCode rootProject.ext.android.versionCode
            versionName rootProject.ext.android.versionName
            multiDexEnabled rootProject.ext.android.multiDexEnabled //突破应用方法数65535的一个限制
        }
    
        signingConfigs {
            //签名打包
            release {
                storeFile file(rootProject.ext.signConfigs.storeFile)
                storePassword rootProject.ext.signConfigs.storePassword
                keyAlias rootProject.ext.signConfigs.keyAlias
                keyPassword rootProject.ext.signConfigs.keyPassword
            }
        }
    
        buildTypes {
            release {
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.release
            }
        }
    
        lintOptions {
            abortOnError false
            lintConfig file("lint.xml")
        }
    
        //recommend
        dexOptions {
            jumboMode = true
        }
    
        compileOptions {
            sourceCompatibility rootProject.ext.java.javaVersion
            targetCompatibility rootProject.ext.java.javaVersion
        }
    
        sourceSets {
            main {
                jniLibs.srcDirs = ['libs'] //修改so库存放位置
            }
        }
    }
    
    //为应用程序添加第三方库依赖
    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
        compile rootProject.ext.dependence.libSupportV7
        compile rootProject.ext.dependence.libSupportMultidex
        compile project(rootProject.ext.dependence.libPullAlive)
        compile rootProject.ext.dependence.libCircleImageView
        compile rootProject.ext.dependence.libSystembarTint
        //添加友盟统计
        compile rootProject.ext.dependence.libUmengAnalytics
        compile rootProject.ext.dependence.libUniversalImageLoader
        compile rootProject.ext.dependence.libOkhttp
        //okttp依赖
        //compile 'com.github.chrisbanes:PhotoView:1.3.0'
        compile(rootProject.ext.dependence.libAutoScrollViewPager) {
            exclude module: 'support-v4' //排除依赖
            exclude group: 'com.android.support' //排除该组下所有的依赖
            transitive false //禁止传递依赖
        }
        compile rootProject.ext.dependence.libSlidableActivity
        //滑动关闭Activity库
        compile rootProject.ext.dependence.libAndfix
        //阿里热修复andfix
        compile rootProject.ext.dependence.libLogger
        //日志库logger
        //Tinker相关依赖
        compile(rootProject.ext.dependence.libTinker) {
            changing = true //每次都从服务端拉取
        }
        provided(rootProject.ext.dependence.libTinkerAndroid) { changing = true }
    }
    

    这是配置后,我们的app工程的build.gradle文件更有语义性,版本变化后也便于管理,同时还可以复用。

    案例二:自动维护版本发布文档

    我们APP每次迭代需要记录APP的版本信息和对应的变化信息,以某一特定的格式记录,方便以后对比管理。下面先看一下版本发布文档的样式:(当然你也可以定义自己想要的样式)

    <releases>
      <release>
        <versionCode>100</versionCode>
        <versionName>1.0.0</versionName>
        <versionInfo>App的第1个版本,上线了一些最基础核心的功能.</versionInfo>
      </release>
    
    <release>
      <versionCode>101</versionCode>
      <versionName>1.0.1</versionName>
      <versionInfo>App的第2个版本,修复了XXXXXBug,优化XXXX功能</versionInfo>
    </release>
      .................. 省略 ...................
    <release>
      <versionCode>110</versionCode>
      <versionName>1.1.0</versionName>
      <versionInfo>第10个版本。。。</versionInfo>
    </release>
    </releases>
    

    有了这样的文档我们可以很方便的对比每个版本的差异,我们可以写一个脚本来自动生成这样的文档,而不需要手动维护。

    releaseinfo.gradle
    import groovy.xml.MarkupBuilder
    
    /**
     * 描述:版本发布文档自动维护脚本
     * 流程描述: 
     *           1、将版本相关信息解析出来
     *           2、将解析出的数据生成xml格式数据
     *           3、写入到已有的文档数据中
     **/
    ext {
        versionName = rootProject.ext.android.versionName
        versionCode = rootProject.ext.android.versionCode
        versionInfo = 'App的第2个版本,上线了一些最基础核心的功能.'
        destFile = file('releases.xml')//指定输出文件
        if (destFile != null && !destFile.exists()) {
            destFile.createNewFile()
        }
    }
    //创建一个Task,并指定输入输出
    task writeTask {
        inputs.property('versionCode', this.versionCode)
        inputs.property('versionName', this.versionName)
        inputs.property('versionInfo', this.versionInfo)
        outputs.file this.destFile
        doLast {
            //将输入的内容写入到输出文件中去
            def data = inputs.getProperties()
            File file = outputs.getFiles().getSingleFile()
            def versionMsg = new VersionMsg(data)
            //将实体对象写入到xml文件中
            def sw = new StringWriter()
            def xmlBuilder = new MarkupBuilder(sw)
            if (file.text != null && file.text.size() <= 0) {
                //没有内容
                xmlBuilder.releases {
                    release {
                        versionCode(versionMsg.versionCode)
                        versionName(versionMsg.versionName)
                        versionInfo(versionMsg.versionInfo)
                    }
                }
                //直接写入
                file.withWriter { writer -> writer.append(sw.toString())
                }
            } else {
                //已有其它版本内容
                xmlBuilder.release {
                    versionCode(versionMsg.versionCode)
                    versionName(versionMsg.versionName)
                    versionInfo(versionMsg.versionInfo)
                }
                //插入到最后一行前面
                def lines = file.readLines()
                def lengths = lines.size() - 1
                file.withWriter { writer ->
                    lines.eachWithIndex { line, index ->
                        if (index != lengths) {
                            writer.append(line + '\r\n')
                        } else if (index == lengths) {
                            writer.append('\r\r\n' + sw.toString() + '\r\n')
                            writer.append(lines.get(lengths))
                        }
                    }
                }
            }
        }
    }
    //信息实体类
    class VersionMsg {
        String versionCode
        String versionName
        String versionInfo
    }
    //通过输入输出来指定Task的执行顺序
    task readTask {
        //指定输入文件为上一个task的输出
        inputs.file this.destFile
        doLast {
            //读取输入文件的内容并显示
            def file = inputs.files.singleFile
            println file.text
        }
    }
    
    task taskZ {
        dependsOn writeTask, readTask//通过依赖指定Task的执行顺序
        doLast {
            println '输入输出任务结束'
        }
    }
    
    //将文档复制到指定的文件中
    task handleReleaseFile {
        def srcFile = file('releases.xml')
        def destDir = new File(this.buildDir, 'generated/release/')
        doLast {
            println '开始解析对应的xml文件...'
            destDir.mkdir()
            def releases = new XmlParser().parse(srcFile)
            releases.release.each { releaseNode ->
                //解析每个release结点的内容
                def name = releaseNode.versionName.text()
                def versionCode = releaseNode.versionCode.text()
                def versionInfo = releaseNode.versionInfo.text()
                //创建文件并写入结点数据
                def destFile = new File(destDir, "release-${name}.txt")
                destFile.withWriter { writer -> writer.write("${name} -> ${versionCode} -> ${versionInfo}")
                }
            }
        }
    
    }
    

    当版本改变的时候,我们只需要修改versionInfo 对应的内容,然后执行Task就可以生成对应的文档了,不过上述脚本还是需要我们手动修改versionInfo 的内容,我们可以把versionInfo 的内容从服务器获取(获取版本信息),这样的话就不需要我们手动修改了,groovy对网络访问这块没有特殊的扩展,所以我们用java的请求网络的方式实现即可。

    案例三:项目发送到远程maven库

    //是否需要把项目发送到远程maven库
    ext.publishToRemote = true
    ext.publishDefaultArtifact = !"true".equals(project.getProperties().get("org.gradle.parallel"))
    ext.publishApk = false
    ext.isApplication = false
    
    if (!project.getBuildFile().exists()) {
        return;
    }
    
    apply plugin: 'maven'
    apply plugin: 'maven-publish'
    
    configurations {
        providedCompile
        compile.extendsFrom providedCompile
    }
    
    repositories {
    //  mavenLocal() :maven本地仓库
        maven {
            url "maven仓库地址"
        }
    }
    
    
    if (!project.getRootProject().hasProperty("aarMap")) {
        project.getRootProject().ext.set("aarMap", new HashSet<String>())
    }
    
    def deployVersion = System.getProperty('deployVersion')
    //配置完成之后执行
    project.afterEvaluate {
        if (project.plugins.hasPlugin("com.android.library")) {
            project.getRootProject().aarMap.add(project.name)
        }
    
        ext.isApplication = (project.plugins.hasPlugin("com.android.application"))
    
        tasks.whenTaskAdded { task ->
            if (task.name.startsWith("generatePomFileForMavenPublication")) {
                task.doFirst {
                    project.publishing.publications.maven(MavenPublication) {
                        if (!components.hasWithName("java") && !isApplication) {
    
                            File f = file("${project.buildDir}/outputs/awb/${project.name}-release.awb");
    
                            if (!f.exists()) {
                                f = file("${project.buildDir}/outputs/aar/${project.name}-release.aar");
                            }
                            if (!f.exists()) {
                                f = file("${project.buildDir}/outputs/awb/${project.name}-debug.awb");
                            }
                            if (!f.exists()) {
                                f = file("${project.buildDir}/outputs/aar/${project.name}-debug.aar");
                            }
    
                            artifact f.getPath()
                        }
                    }
                }
            }
    
            if (isApplication && !publishApk) {
                if (task.name.startsWith("publish")) {
                    task.setEnabled(false)
                }
            }
        }
    }
    
    
    def HashMap getAccount() {
        HashMap accountMap = new HashMap()
        def parsedSettingsXml
        def settingsFile = '/Users/root/Downloads/android/apache-maven-3.3.9/conf/settings.xml'
        def defaultSettingsFile = System.getProperty("user.home") + "/.m2/settings.xml"
    
        if (file(settingsFile).exists() || file(defaultSettingsFile).exists()) {
            if (file(settingsFile).exists()) {
                parsedSettingsXml = (new XmlParser()).parse(settingsFile);
            } else if (file(defaultSettingsFile).exists()) {
                parsedSettingsXml = (new XmlParser()).parse(defaultSettingsFile);
            }
    
            parsedSettingsXml.servers[0].server.each { server ->
                if ("releases" == server.id.text()) {
                    accountMap.put("id", server.id.text())
                    accountMap.put("username", server.username.text())
                    accountMap.put("password", server.password.text())
                }
            }
    
        } else {
            accountMap.put("id", "releases")
            accountMap.put("username", "admin")
            accountMap.put("password", "admin123")
        }
        return accountMap
    }
    
    publishing {
        if (null != deployVersion) {
            version = deployVersion
        }
    
        publications {
            maven(MavenPublication) {
                version version
                task sourceJar(type: Jar) {
                    classifier = 'source'
                    version = version
                    try {
                        if (components.hasWithName("java")) {
                            from sourceSets.main.allJava
                        } else {
                            from android.sourceSets.main.java.srcDirs
                        }
                    } catch (Throwable e) {
                    }
                }
                artifact sourceJar
                if (components.hasWithName("java") || components.hasWithName("android")) {
    
                    if (components.hasWithName("java")) {
                        from components.java
                    } else if (!isApplication) {
                        from components.android
                    }
    
                    pom.withXml {
                        asNode().dependencies.'*'.each {
                            if (it.scope.text() == 'runtime') {
                                if (project.configurations.providedCompile.allDependencies.find { dep -> dep.name == it.artifactId.text() }) {
                                    it.scope*.value = 'provided'
                                } else if (project.configurations.compile.allDependencies.find { dep -> dep.name == it.artifactId.text() }) {
                                    it.scope*.value = 'compile'
                                }
                            }
                        }
                    }
    
                    pom.withXml {
                        asNode().dependencies.'*'.findAll() {
                            it.groupId.text() == groupId && project.getRootProject().aarMap.contains(it.artifactId.text())
                        }.each { it.appendNode('type', 'aar') }
                    }
    
                    if (!components.hasWithName("java") && !isApplication) {
                        pom.withXml {
                            def dependenciesNode = asNode().dependencies[0]
                            if (getGradle().startParameter.toString().contains("assembleDebug")) {
                                configurations.debugCompile.allDependencies.each {
                                    if (it.group != null && (it.name != null || "unspecified".equals(it.name)) && it.version != null) {
    
                                        def dependencyNode = dependenciesNode.appendNode('dependency')
                                        dependencyNode.appendNode('groupId', it.group)
                                        dependencyNode.appendNode('artifactId', it.name)
                                        dependencyNode.appendNode('version', it.version)
                                        if (it.group == groupId && project.getRootProject().aarMap.contains(it.name)) {
                                            dependencyNode.appendNode('type', 'aar')
                                        }
                                    }
                                }
                            } else {
                                configurations.releaseCompile.allDependencies.each {
                                    if (it.group != null && (it.name != null || "unspecified".equals(it.name)) && it.version != null) {
    
                                        def dependencyNode = dependenciesNode.appendNode('dependency')
                                        dependencyNode.appendNode('groupId', it.group)
                                        dependencyNode.appendNode('artifactId', it.name)
                                        dependencyNode.appendNode('version', it.version)
                                        if (it.group == groupId && project.getRootProject().aarMap.contains(it.name)) {
                                            dependencyNode.appendNode('type', 'aar')
                                        }
    
                                    }
                                }
                            }
                        }
                    }
    
                } else if (!isApplication) {
    
                    pom.withXml {
    
                        def dependenciesNode = asNode().appendNode('dependencies')
    
                        def providedCompiles = new HashSet();
    
                        configurations.providedCompile.allDependencies.each {
                            if (it.group != null && (it.name != null || "unspecified".equals(it.name)) && it.version != null) {
                                def dependencyNode = dependenciesNode.appendNode('dependency')
                                dependencyNode.appendNode('groupId', it.group)
                                dependencyNode.appendNode('artifactId', it.name)
                                dependencyNode.appendNode('version', it.version)
                                dependencyNode.appendNode('scope', 'provided')
                                if (it.group == groupId && project.getRootProject().aarMap.contains(it.name)) {
                                    dependencyNode.appendNode('type', 'aar')
                                }
                                providedCompiles.add(it.group + "." + it.name)
                            }
                        }
    
                        configurations.compile.allDependencies.each {
                            if (it.group != null && (it.name != null || "unspecified".equals(it.name)) && it.version != null) {
    
                                if (!providedCompiles.contains(it.group + "." + it.name)) {
                                    def dependencyNode = dependenciesNode.appendNode('dependency')
                                    dependencyNode.appendNode('groupId', it.group)
                                    dependencyNode.appendNode('artifactId', it.name)
                                    dependencyNode.appendNode('version', it.version)
                                    if (it.group == groupId && project.getRootProject().aarMap.contains(it.name)) {
                                        dependencyNode.appendNode('type', 'aar')
                                    }
                                }
                            }
                        }
    
                        if (getGradle().startParameter.toString().contains("assembleDebug")) {
                            configurations.debugCompile.allDependencies.each {
                                if (it.group != null && (it.name != null || "unspecified".equals(it.name)) && it.version != null) {
                                    if (!providedCompiles.contains(it.group + "." + it.name)) {
                                        def dependencyNode = dependenciesNode.appendNode('dependency')
                                        dependencyNode.appendNode('groupId', it.group)
                                        dependencyNode.appendNode('artifactId', it.name)
                                        dependencyNode.appendNode('version', it.version)
                                        if (it.group == groupId && project.getRootProject().aarMap.contains(it.name)) {
                                            dependencyNode.appendNode('type', 'aar')
                                        }
                                    }
                                }
                            }
                        } else {
                            configurations.releaseCompile.allDependencies.each {
                                if (it.group != null && (it.name != null || "unspecified".equals(it.name)) && it.version != null) {
    
                                    if (!providedCompiles.contains(it.group + "." + it.name)) {
                                        def dependencyNode = dependenciesNode.appendNode('dependency')
                                        dependencyNode.appendNode('groupId', it.group)
                                        dependencyNode.appendNode('artifactId', it.name)
                                        dependencyNode.appendNode('version', it.version)
                                        if (it.group == groupId && project.getRootProject().aarMap.contains(it.name)) {
                                            dependencyNode.appendNode('type', 'aar')
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    
        repositories {
            mavenLocal()
            if (publishToRemote) {
                if (version.endsWith("-SNAPSHOT")) {
                    maven {
                        url "maven仓库远程地址"
                        credentials {
                            username = "admin"
                            password = "admin123"
                        }
                    }
                } else {
                    def accountMap = getAccount();
                    maven {
                        url "maven仓库本地地址"
                        credentials {
                            username = accountMap.get("username")
                            password = accountMap.get("password")
                        }
                    }
                }
            }
        }
    }
    

    案例四:多渠道打包

    渠道包就是要在安装包中添加渠道信息,也就是channel,对应不同的渠道,例如:小米市场、360市场、应用宝市场等 。我们要在安装包中添加不同的标识,应用在请求网络的时候携带渠道信息,方便后台做运营统计(这就是添加渠道信息的用处)。

    在AndroidMainfest.xml配置相应的渠道
    <meta-data android:value="UMENG_CHANNEL"         
          android:name="${UMENG_CHANNEL_VALUE}"/>  <!--动态更改渠道号-->
    
    在build.gradle中配置渠道信息和自动替换脚本
    //多渠道打包
    productFlavors {
        xiaomi {}
        huawei {}
        yingyongbao {}
        wandoujia {}
    }
    
    //自动替换清单文件中的渠道号
    productFlavors.all {
        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
    }
    
    默认配置
    defaultConfig {
        applicationId "com.gradle.demo"
        minSdkVersion 11
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        multiDexEnabled true //突破应用方法数65535的限制
    }
    

    所有渠道默认使用这一配置,如果渠道有特殊需求,可以在productFlavors对应的渠道号中单独配置。

    打包后自动修改APK的名字
    //release包的命名格式为:产品名_版本号_渠道名.apk
    //debug包的命名格式为:产品名_版本号-debug.apk
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            if (null != outputFile && outputFile.name.endsWith('.apk')) {
                File outputDir = new File(outputFile.parent);
                def baseName = PRODUCT_NAME + "${defaultConfig.versionName}" + "_" + variant.productFlavors[0].name
                def newApkName
                if (variant.buildType.name.equals('release')) {
                    newApkName = baseName + '.apk'
                } else if (variant.buildType.name.equals('debug')) {
                    def debugName = PRODUCT_NAME + "${defaultConfig.versionName}"
                    newApkName = debugName + "-debug.apk"
                }
                output.outputFile = new File(outputDir, newApkName)
            }
        }
    }
    
    完整build.gradle文件内容如下:
    apply plugin: 'com.android.application'
    
    //产品名
    def PRODUCT_NAME = "BuglyDemo"
    
    android {
        //添加签名文件配置
        signingConfigs {
            mysigns {
                keyAlias 'alias'
                keyPassword '123456'
                storeFile file('sign.jks')
                storePassword '123456'
            }
        }
    
        compileSdkVersion 25
        buildToolsVersion "25.0.2"
    
        defaultConfig {
            applicationId "com.gradle.demo"
            minSdkVersion 11
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            multiDexEnabled true //突破应用方法数65535的限制
        }
    
        //多渠道打包
        productFlavors {
            xiaomi {}
            huawei {}
            yingyongbao {}
            wandoujia {}
        }
    
        //自动替换清单文件中的渠道号
        productFlavors.all {
            flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
        }
        
    
        buildTypes {
            release {
                minifyEnabled false //是否启用混淆
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                debuggable false
                jniDebuggable false
                signingConfig signingConfigs.mysigns
                renderscriptDebuggable false
                minifyEnabled false
                pseudoLocalesEnabled false
                zipAlignEnabled true
            }
        }
    
    //release包的命名格式为:产品名版本号渠道名.apk
    //debug包的命名格式为:产品名_版本号-debug.apk
        applicationVariants.all { variant ->
            variant.outputs.each { output ->
                def outputFile = output.outputFile
                if (null != outputFile && outputFile.name.endsWith('.apk')) {
                    File outputDir = new File(outputFile.parent);
                    def baseName = PRODUCT_NAME + "${defaultConfig.versionName}" + "_" + variant.productFlavors[0].name
                    def newApkName
                    if (variant.buildType.name.equals('release')) {
                        newApkName = baseName + '.apk'
                    } else if (variant.buildType.name.equals('debug')) {
                        def debugName = PRODUCT_NAME + "${defaultConfig.versionName}"
                        newApkName = debugName + "-debug.apk"
                    }
                    output.outputFile = new File(outputDir, newApkName)
                }
            }
        }
    }
    
    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:appcompat-v7:25.1.1'
        testCompile 'junit:junit:4.12'
        //添加友盟统计库依赖
        compile 'com.umeng.analytics:analytics:latest.integration'
    }
    

    打包工具:ApkChannelPackag

    关注微信公众号获取更多相关资源

    Android行动派

    相关文章

      网友评论

        本文标题:Gradle实战

        本文链接:https://www.haomeiwen.com/subject/buugpftx.html