看不懂的 build.gradle 代码

本文深入解析了Android项目中复杂的build.gradle文件,包括gradle插件应用、自定义方法、签名配置、编译选项等内容,并解答了开发者在实际项目中遇到的一些疑问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android Studio 这么强大的工具,就算我们不懂 gradle, groovy, 也照样能借助AS对 Android 项目进行编译、调试、运行、打包等操作。build.gradle 这个文件接触这么久了,基本的项目配置也基本很熟悉了,毕竟每次自动创建的 build.gradle 里的代码就那么几项配置,看一下那些英文单词也基本猜到是什么配置。

但是,不知道你们会不会跟我一样,在 github 上 clone 大神的项目后,总会发现他们的 build.gradle 里多了很多平常没看见过的代码,而且还看不懂代码要做什么;

或者是比如当需要进行签名时,网上资料会让你在 Android 标签内加个 signingConfigs, 然后在它里面进行各种配置,比如 storeFile, keyAlias 等等之类的。还有其他类似这种情况,比如当需要打包时,在哪个地方加个什么标签再对它进行各种配置之类的。不知道你们会不会也跟我一样会有这样的疑问,这个标签名怎么来的,为什么要放在这个位置,它里面有哪些属性可以进行配置?

疑惑久了,总想去了解下这是为什么,所以花了一段时间来学习 gradle 的相关知识,这次在这里记录也分享一下,如果有错的地方,还望指点一下。

本次计划是写个 gradle 系列博客,大概会有3-4篇,第一篇只是简单的针对某个具体的 build.gradle 文件代码进行注释解释以及抛出一些疑问,当然这个 build.gradle 不会是AS自动创建的那么简单的代码。然后再写1-2篇介绍 gradle, groovy, 相关的资料网上很多,所以不会写得很基础,大概是挑选一些我认为比较重要的知识点进行介绍。最后在前面的基础上,对 build.gradle 里面的代码进行分析讲解,比如介绍说都有哪些标签,哪里去找这些标签等等。

好了,废话就唠叨到这,下面就开始正文。


系列索引

build.gradle系列一:看不懂的build.gradle代码
build.gradle系列二:学点Groovy来理解build.gradle代码
build.gradle系列三:如何用Android Studio查看build.gradle源码


build.Gradle

这个 build.Gradle 文件来自 drakeet 大神的 Meizi 项目
我直接在代码上加注释,参照着注释看代码就行,是不是发现有很多代码平时都没看见过。

//Model都有各自的build.gradle,这里声明该Model作为主项目,常见的还有另一个取值:
//apply plugin: 'com.android.library' 声明该Model作为库使用,当然还有其他取值,后面博客会介绍
apply plugin: 'com.android.application'

//这里是在as里引入一个retrolambda插件,具体我也不大懂,可以看看这篇博客: 
//http://blog.youkuaiyun.com/zhupumao/article/details/51934317?locationNum=12
apply plugin: 'me.tatarka.retrolambda'

//这里是groovy的代码了,定义了一个获取时间的方法,groovy是兼容java,它可以直接使用jdk里的方法
def releaseTime() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

//file()是Project.java里的一个方法,这里定义一个File类型的对象,Project后面博客会介绍到
def keyStore = file('meizhi.keystore')

android {

    //这个大家应该很熟悉了,有疑问的应该是后面的代码,这里表示获取一些全局变量
    //这些变量的值在根目录下的build.gradle中定义,具体可以看看这篇博客:
    //http://blog.youkuaiyun.com/fwt336/article/details/54613419
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    buildToolsVersion rootProject.ext.android.buildToolsVersion

    //同理,这里都是通过获取全局设置的变量值来进行相关配置,这样做的好处在于当
    //你的项目里有多个model时,可以方便修改这些公共的配置,只需要修改一个地方就可以同步了
    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
    }

    //这里应该是设置打包后的apk里的META-INF移除指定的文件吧
    packagingOptions {
        exclude 'META-INF/DEPENDENCIES.txt'
        //省略部分exclude 代码...
    }

    //关闭指定的lint检查
    lintOptions {
        disable 'MissingTranslation', 'ExtraTranslation'
    }

    //lint检查到错误时不中断编译,好像是说lint检查是为优化代码,发现的错误其实并不会导致程序异常
    //所以有的时候及时发现Lint检查错误还是可以直接运行查看效果
    lintOptions {
        abortOnError false
    }

    //签名的相关配置
    signingConfigs {
        //这个标签名可以随意命名,这里的作用大概类似于定义一个对象,该对象里设置好了签名需要的各种配置
        //可以定义不止一种配置的签名对象,例如常见的还有 debug{...}, release{...},然后在buildTypes{}里
        //通过 signingConfigs.app1 进行调用
        app1 {
            //签名的相关配置,网上资料很多,STOREPASS, KEYALIAS, KEYPASS 这些常量是定义在
            //gradle.properties 文件里,如果没有该文件手动创建即可,这样可以保证安全
            //只有定义在 gradle.properties 里的常量才可以直接通过常量名引用
            storeFile file('meizhi.keystore')
            storePassword project.hasProperty('STOREPASS') ? STOREPASS : ''
            keyAlias project.hasProperty('KEYALIAS') ? KEYALIAS : ''
            keyPassword project.hasProperty('KEYPASS') ? KEYPASS : ''
        }
    }

    //编译,打包的项目配置
    buildTypes {

        debug {
            //在 BuildConfig 里自定义一个 boolean 类型的常量
            //更多资料可以查看:http://stormzhang.com/android/2015/01/25/gradle-build-field/ 
            buildConfigField "boolean", "LOG_DEBUG", "true"

            debuggable true
            applicationIdSuffix ".debug"
        }

        release {
            buildConfigField "boolean", "LOG_DEBUG", "false"

            debuggable false

            //开启混淆
            minifyEnabled true
            //删除无用的资源
            shrinkResources true
            //混淆文件
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            if (keyStore.exists()) {
                println "Meizhi: using drakeet's key"
                //根据在signingConfigs.app1里的签名配置进行签名
                signingConfig signingConfigs.app1
            } else {
                println "Meizhi: using default key"
            }

            //这段代码应该会在大神的项目里挺常见的,我在很多项目里都看见过了
            //这也是groovy的代码,这里的代码作用是重命名最后打包出来的apk
            //根据 def fileName 设置的格式来命名,${}表示的是某个变量的引用
            //例如根据设置的格式最后apk命名可能是: Meizhi_v1.0.0_2017-03-28_fir.apk
            //至于 applicationVariants 这些变量含义后面博客会介绍
            applicationVariants.all { variant ->
                variant.outputs.each { output ->
                    def outputFile = output.outputFile
                    if (outputFile != null && outputFile.name.endsWith('.apk')) {
                        def fileName = "Meizhi_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
                        output.outputFile = new File(outputFile.parent, fileName)
                    }
                }
            }
        }

        //这里的作用跟 singingConfigs 差不多,只是为不同的 flavor 设置一些属性
        //常见的设置比如设置不同的渠道编号,设置不同的 api 服务器等等
        productFlavors {
            fir {
                //这个的作用是将 AndroidManifest.xml 里的占位符 ¥{UMENG_CHANNEL_VALUE} 的值替换成fir
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "fir"]
            }
            GooglePlay {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "GooglePlay"]
            }
            Umeng {
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "Umeng"]
            }
        }
    }

    //设置JDK的版本通过compileOptions
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    //lint的相关配置吧
    lintOptions {
        disable "InvalidPackage"
        lintConfig file("lint.xml")
    }
}

//这里就不用说了
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(":libraries:headsupcompat")
    compile project(":libraries:smooth-app-bar-layout")
    //as默认会去下载传递依赖,下面是指定不需要去下载传递依赖
    compile ('com.squareup.retrofit2:retrofit:2.1.0') {
        exclude module: 'okhttp'
    }
    retrolambdaConfig 'net.orfjackal.retrolambda:retrolambda:2.3.0'
    //省略部分compile代码...
}


疑问

1.apply plugin: ‘com.android.application’ 听说这是调用一个方法?

2.rootProject.ext.android.compileSdkVersion, 不用 ext 来设置全局变量是否可以?

3.defaultConfig{}, packagingOptions{}, signingConfigs{}, buildTypes{} 等等这些,我怎么知道 Android{} 里都有哪些可以使用?

参考资料

·徐宜生写的《Android群英传:神兵利器》第4章:与Gradle的爱恨情仇
·retrolambda使用教程
·Gradle配置全局变量
·GRADLE自定义你的BUILDCONFIG


QQ图片20180316094923.jpg
最近刚开通了公众号,想激励自己坚持写作下去,初期主要分享原创的Android或Android-Tv方面的小知识,感兴趣的可以点一波关注,谢谢支持~~

<think>好的,我现在需要帮助用户解决他们在Android项目中遇到的构建错误:“Could not find method ndkVersion() for arguments [21.3.6528147]”。用户之前已经询问过类似的问题,但这次他们强调自己是新手,希望得到更详细的指导,明确指出需要修改的地方,不要省略任何代码,并且不要做不必要的改动。 首先,我需要仔细分析用户提供的build.gradle文件内容,找出导致错误的原因。根据之前的回答,问题通常是由于在错误的块中配置了ndkVersion。用户可能是在android块下直接使用了ndkVersion,而新版本的Gradle插件需要将ndk配置放在defaultConfig内的ndk块中。 查看用户提供的代码,发现他们的build.gradle文件中并没有ndkVersion的配置。这可能意味着用户可能在另一个文件(比如模块级的build.gradle)中添加了ndkVersion,或者之前的尝试已经被移除。不过,用户当前的代码中没有这一行,但错误仍然存在,这说明可能有其他问题。 接下来,我需要检查用户的Gradle插件版本。用户当前的buildscript中的Gradle插件版本是3.0.0,这是一个较旧的版本。旧版本的插件可能不支持某些NDK配置方式,或者需要特定的语法。例如,在Gradle插件3.0.0中,可能需要在android块中直接指定ndk版本,而不是在defaultConfig里。 但根据之前的错误信息,用户可能在某个地方调用了ndkVersion方法,而当前提供的代码中并没有。因此,用户可能在之前的尝试中添加过ndkVersion,但现在已经删除了,但Gradle缓存导致问题仍然存在。或者用户可能在另一个模块的build.gradle文件中配置了ndkVersion。 另一个可能性是用户的项目中使用了过时的依赖库版本,导致兼容性问题。例如,用户使用的appcompat-v7是26.0.0-beta1,而recyclerview-v7是28.0.0,版本不一致可能导致冲突。此外,Gradle插件3.0.0可能不支持较新的依赖库版本。 用户还可能遇到了Gradle版本与插件版本不兼容的问题。Gradle 3.0.0需要对应特定版本的Gradle分发版本,比如Gradle 4.1。如果用户的gradle-wrapper.properties中指定的Gradle版本不兼容,也会导致构建错误。 另外,用户提到自己是小白,可能对Gradle配置不太熟悉,所以需要给出明确的修改步骤,并指出具体修改的位置,避免混淆。需要确保用户能够准确找到需要修改的代码行,并替换正确的内容。 总结需要检查的方面: 1. NDK配置的位置是否正确。 2. Gradle插件版本是否过旧,是否需要升级。 3. 依赖库版本是否一致且兼容。 4. Gradle分发版本是否与插件版本兼容。 5. 是否存在拼写错误或语法错误。 接下来,根据用户提供的build.gradle内容,没有发现ndkVersion的配置,但错误提示存在,所以可能需要用户检查其他文件,或者可能存在其他配置问题。不过,用户提供的代码中确实存在其他问题,比如依赖库版本不一致,Gradle插件版本较旧,这些都可能引发构建问题。 因此,需要指导用户升级Gradle插件版本,统一支持库版本,并确保NDK配置正确。同时,需要详细标注修改的位置,避免用户混淆。例如,将Gradle插件升级到较新的版本(如3.6.0),并将所有支持库版本统一为26.0.0,解决版本冲突问题。此外,如果用户确实需要配置NDK,应指导他们在正确的块中添加ndkVersion,或者在升级插件后使用新的配置方式。</think>好的!我会 **直接标注需要修改的位置**,并 **只调整导致错误的部分**,其他代码保持原样。根据你的文件内容,错误可能有两个原因: --- ### 🔧 **需要修改的地方 1:NDK 配置问题** 你的代码中 **没有直接出现 `ndkVersion`**,但根据之前的报错信息推测,你可能在另一个模块(如 `build.gradle (Module: app)`)中添加过类似代码。如果确实需要配置 NDK 版本,**修改方式如下**: 1. **定位到 `android > defaultConfig` 块**,在其中添加 `ndk` 子块: ```groovy android { compileSdkVersion 26 defaultConfig { applicationId "com.example.dell.recyclerview" minSdkVersion 15 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // ▼ 新增 ndk 配置 ▼ ndk { version "21.3.6528147" // 改成你的 NDK 版本 } // ▲ 新增结束 ▲ } // 其他代码保持不变... } ``` --- ### 🔧 **需要修改的地方 2:依赖库版本冲突** 你的 `recyclerview` 版本与其他库不一致(`appcompat-v7` 是 `26.0.0-beta1`,而 `recyclerview` 是 `28.0.0`),这可能导致构建错误。**统一版本号**: ```groovy dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.0.0-beta1' implementation 'com.android.support.constraint:constraint-layout:1.0.2' // ▼ 修改 recyclerview 版本 ▼ implementation 'com.android.support:recyclerview-v7:26.0.0-beta1' // 保持与 appcompat 一致 // ▲ 修改结束 ▲ testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:0.5' androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2' } ``` --- ### 🔧 **需要修改的地方 3:Gradle 插件版本过旧** 你的 Gradle 插件版本是 `3.0.0`(2017年发布),可能导致兼容性问题。**建议升级到稳定版本**(例如 `3.6.0`): ```groovy buildscript { repositories { jcenter() } dependencies { // ▼ 修改插件版本 ▼ classpath 'com.android.tools.build:gradle:3.6.0' // 旧版本是 3.0.0 // ▲ 修改结束 ▲ } } ``` --- ### ✅ **最终修复后的完整文件** ```groovy apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.example.dell.recyclerview" minSdkVersion 15 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // ▼ 新增 ndk 配置(如果不需要 NDK,删除此块) ▼ ndk { version "21.3.6528147" } // ▲ 新增结束 ▲ } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.0.0-beta1' implementation 'com.android.support.constraint:constraint-layout:1.0.2' // ▼ 修改 recyclerview 版本 ▼ implementation 'com.android.support:recyclerview-v7:26.0.0-beta1' // ▲ 修改结束 ▲ testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:0.5' androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2' } buildscript { repositories { jcenter() } dependencies { // ▼ 修改插件版本 ▼ classpath 'com.android.tools.build:gradle:3.6.0' // ▲ 修改结束 ▲ } } allprojects { repositories { jcenter() } } ``` --- ### ⚠️ **注意事项** 1. **如果不需要使用 NDK**,直接删除 `ndk` 配置块即可。 2. 修改后点击 Android Studio 右上角的 **Sync Now** 同步项目。 3. 如果仍有错误,请检查是否在项目其他文件(如 `gradle.properties` 或 `settings.gradle`)中配置了 NDK。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值