自定义plugin+including构建项目实战

时间:2021-7-20 作者:qvyue

01.前言

在实际项目中,我一直想怎样以一种优雅的方式,解决build.gradle配置文件问题,我想你和我一定有同样的感受,在组件化亦或者模块化开发中,我相信我们都会进行同一种操作,为每个module配置一个build.gradle文件来构建我们的工程,但配置不能出错,版本得统一等许多细节,一旦出错,项目就无法构建。

02.为什么使用plugin方式

正如前言提到,我们在构建项目时,都要为module配置build.gradle项,繁琐,要细心,不能出错,并且文件还臭长,不易阅读,不易查找。为了解决这一问题,通过学习了解发现,自定义plugin就可以完美解决,我们可以像写业务代码一样,编写我们的gradle插件。

03.项目地址

https://gitee.com/zhuhuitao/componentization

04.传统apply from 引入方式

为了对每个module版本进行统一,会建一个config.gradle文件如下:

ext {
    android = [
            compileSdkVersion   : 29,
            buildToolsVersion   : "30.0.2",
            applicationId       : "com.xionggouba.bearseller",
            minSdkVersion       : 19,
            targetSdkVersion    : 30,
            versionCode         : 27,
            versionName         : "3.9.1",
            defaultPublishConfig: 'release',
            publishNonDefault   : true,
            multiDexEnabled     : true,
            mapKey              : 'c7e1ee468aa1bf8a6739',
            pushKey             : '65aae199a0059eb1dbe7',
            pushChannel         : 'developer-default',
    ]
    appid = [
            app           : "com.xionggouba.bearseller",
            login         : "com.huitao.login",
            home          : "com.huitao.home",
            webview       : "com.huitao.webview",
            main          : "com.huitao.main",
            productManager: "com.huitao.productmanager",
            personal      : "com.huitao.personalcenter",
            map           : "com.huitao.map",
            bluetooth     : "com.huitao.bluetooth",
            push          : "com.huitao.push",
            markketing    : "con.huitao.marketing",
            printer       : "com.huitao.printer"
    ]
    versions = [
            "lifecycle_version": "2.2.0",
            "arch_version"     : "2.1.0",
            "retrofit_version" : "2.6.2",
            "dialog_version"   : "3.3.0",
            "glide_version"    : "4.9.0",
            "hilt"             : "2.28-alpha",
            "kotlin_version"   : "1.4.10",
            "fragment_version" : "1.2.5",
            "room_version"     : "2.2.6"
    ]
    architecture = [
            "viewmodel"          : "androidx.lifecycle:lifecycle-viewmodel-ktx:${versions['lifecycle_version']}",
            "livedata"           : "androidx.lifecycle:lifecycle-livedata-ktx:${versions['lifecycle_version']}",
            "lifecycleruntime"   : "androidx.lifecycle:lifecycle-runtime-ktx:${versions['lifecycle_version']}",
            "savedstate"         : "androidx.lifecycle:lifecycle-viewmodel-savedstate:${versions['lifecycle_version']}",
            // alternately - if using Java8, use the following instead of lifecycle-compiler
            "lifecyclecommon"    : "androidx.lifecycle:lifecycle-common-java8:${versions['lifecycle_version']}",
            // Saved state module for ViewModel
            "viewmodelsavedstate": "androidx.lifecycle:lifecycle-viewmodel-savedstate:${versions['lifecycle_version']}",
            "lifecycleextentions": "androidx.lifecycle:lifecycle-extensions:${versions['lifecycle_version']}",
            "retrofit2"          : "com.squareup.retrofit2:retrofit:${versions['retrofit_version']}",
            "gson"               : "com.squareup.retrofit2:converter-gson:${versions['retrofit_version']}",
            "persistentcookiejar": "com.github.franmontiel:PersistentCookieJar:v1.0.1",
            "glide"              : "com.github.bumptech.glide:glide:${versions['glide_version']}",
            "glidecompiler"      : "com.github.bumptech.glide:compiler:${versions['glide_version']}",
            "oss"                : "com.aliyun.dpa:oss-android-sdk:2.9.1",
            "luban"              : "top.zibin:Luban:1.1.8"
    ]
]

在我们工程的根目录build.gradle

apply from"config.gradle"

这里config文件中仅仅是部分代码,实际项目中远远不止这几行,其实我们都感觉很难阅读,找一个东西就得靠搜索,逐一查找。

05.对比buildSrc方式

这里有讲述buildSrc的缺陷,https://flywith24.gitee.io/2020/04/15/Tips-includeBuild/

06. 自定义plugin+includeBuild

1,通过新建library的方式新建一个version,如下图:

自定义plugin+including构建项目实战
在这里插入图片描述

2,修改version中的build.gradle.kts,我们以kotlin方式编写,如下:

buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
        // 由于使用的 Kotlin 须要须要添加 Kotlin 插件,这里使用最新的kotlin插件
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.32")
    }
}
plugins {
    `kotlin-dsl`
    `java-gradle-plugin`
}
repositories {
    // 须要添加 jcenter 不然会提示找不到 gradlePlugin
    jcenter()
    google()
}
dependencies {
    compileOnly(gradleApi())
    compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.32")
    compileOnly("com.android.tools.build:gradle:4.1.1")
}
kotlinDslPluginOptions {
    experimentalWarning.set(false)
}
gradlePlugin {
    plugins {
        create("version") {
            // 在 app 模块须要经过 id 引用这个插件
            id = "com.huitao.version"
            // 实现这个插件的类的路径
            implementationClass = "com.huitao.version.VersionConfigPlugin"
        }
    }
}

3,在工程setting.gradle中引入version插件:

自定义plugin+including构建项目实战
在这里插入图片描述

然后进行同步,这样便于我们在插件编写kotlin代码有提示,
4,修改工程的builde.gradle.kts,也采用kotlin方式编写:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    id("com.huitao.version") apply (false)
}
buildscript {
    //这里需要和version module中的kotlin版本统一,实际测试中如果不统一好像编译不通过
    val kotlinVersion by extra("1.4.32")
    val android by extra("com.android.tools.build:gradle:4.1.2")
    repositories {
        google()
        jcenter()
    }
    dependencies {
        //kotlin的写法
        classpath(android)
        classpath(kotlin(module = "gradle-plugin", version = kotlinVersion))
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
allprojects {
    repositories {
        google()
        jcenter()
        maven { url = uri("https://jitpack.io") }
    }
}
subprojects {
    // 子项目统一应用插件
    project.apply(plugin = "com.huitao.version")
    when (name) {
        //判断如果module是app则引入com.android.application插件
        "app" -> apply(plugin = "com.android.application")
        //如果不是则引入com.android.library插件,这里可以灵活编写
        else -> apply(plugin = "com.android.library")
    }
}
tasks.register("clean") {
    delete(buildDir)
}

5,新建VersionConfigPlugin 继承Plugin:

/**
 *author  : huitao
 *e-mail  : pig.huitao@gmail.com
 *date    : 2021/5/12 20:52
 *desc    :
 *version :
 */
class VersionConfigPlugin : Plugin {
    override fun apply(project: Project) {
        project.plugins.config(project)
    }
    private fun PluginContainer.config(project: Project) {
        whenObjectAdded {
            when (this) {
                is AppPlugin -> {
                    //公共插件
                    project.configCommonPlugin()
                    //公共android配置
                    project.extensions.getByType().applyAppCommons(project)
                    //公共依赖
                    project.configAppDependencies()
                }
                is LibraryPlugin -> {
                    //公共插件
                    project.configCommonPlugin()
                    //公共android配置
                    project.extensions.getByType().applyLibraryCommons(project)
                    //公共依赖
                    project.configLibraryDependencies()
                }
                is KotlinAndroidPluginWrapper -> {
                    //根据 project build.gradle.kts 中的配置动态设置 kotlinVersion
                    project.getKotlinPluginVersion()?.also { kotlinVersion ->
                        GradlePlugins.kotlinVersion = kotlinVersion
                    }
                }
            }
        }
    }
    ///library module 公共依赖
    private fun Project.configLibraryDependencies() {
        dependencies.apply {
            add(api, fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
            add(implementation, GradlePlugins.kotlinStdlib)
            //如果module 的 name 不是common则引入common模块,不加次判断,活报错,提示我们common
            //module自己引入自己,相当于递归了
            if (name != "common") {
                add(implementation, project(":common"))
            }
            //引入autoService服务,主要为后续的组件化开发做准备,Kotlin中要使用kapt方式
            add(kapt, ThirdParty.autoService)
            add(compileOnly, ThirdParty.autoService)
            configTestDependencies()
        }
    }
    //app module
    private fun Project.configAppDependencies() {
        dependencies.apply {
            add(implementation, fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
            //添加library工程,不用在build中编写
            add(implementation, GradlePlugins.kotlinStdlib)
            add(implementation, (project(":login")))
            add(implementation, project(":order"))
            add(implementation, project(":common"))
            configTestDependencies()
        }
    }
    /// test 依赖配置
    private fun DependencyHandler.configTestDependencies() {
        testImplementation(Testing.testLibraries)
        androidTestImplementation(Testing.androidTestLibraries)
    }
    ///kotlin插件
    private fun Project.configCommonPlugin() {
        //添加android  或者 kotlin插件
        plugins.apply("kotlin-android")
        plugins.apply("kotlin-android-extensions")
        plugins.apply("kotlin-kapt")
    }
    ///app module 配置项 此处固定了applicationId
    private fun AppExtension.applyAppCommons(project: Project) {
        defaultConfig {
            applicationId = BuildConfig.applicationId
        }
        applyBaseCommons(project)
    }
    /// app module 配置项
    private fun LibraryExtension.applyLibraryCommons(project: Project) {
        applyBaseCommons(project)
    }
    ///配置android闭包下的公共环境
    private fun BaseExtension.applyBaseCommons(project: Project) {
        compileSdkVersion(BuildConfig.compileSdkVersion)
        buildToolsVersion(BuildConfig.buildToolsVersion)
        defaultConfig {
            minSdkVersion(BuildConfig.minSdkVersion)
            targetSdkVersion(BuildConfig.targetSdkVersion)
            versionCode = BuildConfig.versionCode
            versionName = BuildConfig.versionName
            testInstrumentationRunner = BuildConfig.runner
            manifestPlaceholders(
                mapOf(
                    jPushPackageName to BuildConfig.applicationId,
                    jPushAppKey to "7e88bec095d30e8ced40654cc",
                    jPushChannel to "developer-default"
                )
            )
        }
        signingConfigs {
            register(BuildConfig.release) {
                keyAlias = "bear"
                keyPassword = "BEARSELLER"
                storePassword = "bearseller"
                storeFile = File("bearSeller.jks")
            }
            /*  register(BuildConfig.debug) {
                  keyAlias("bear")
                  keyPassword("BEARSELLER")
                  storePassword("bearseller")
                  storeFile(File("../bearSeller.jks"))
              }*/
        }
        buildTypes {
            getByName(BuildConfig.release) {
                isMinifyEnabled = false
                signingConfig = signingConfigs.getByName(BuildConfig.release)
                proguardFiles(
                    getDefaultProguardFile("proguard-android-optimize.txt"),
                    "proguard-rules.pro"
                )
            }
            getByName(BuildConfig.debug) {
                signingConfig = signingConfigs.getByName(BuildConfig.debug)
            }
        }
        compileOptions {
            sourceCompatibility = JavaVersion.VERSION_1_8
            targetCompatibility = JavaVersion.VERSION_1_8
        }
        dataBinding {
            isEnabled = true
        }
        project.tasks.withType {
            kotlinOptions {
                jvmTarget = "1.8"
            }
        }
    }
}

6,在common module中的build.gradle中:

dependencies{
    api(AndroidX.constraintlayout)
    api(AndroidX.appcompat)
    api(AndroidX.coreKtx)
    api(AndroidX.material)
}

上面这段代码是可以直接直接写在versionPlugin中的,个人喜欢,你想写在哪就写在哪,
7,验证module是否正确引入了common:
在common library中新建工具类Util,然后在module order中是否可以正确使用:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Util.logD("测试")
    }
}

总结

通过自定义plugin+including的方式,我们可以完全通过kotlin代码方式编写我们的gradle配置,让我们的build.gradle中没有一行代码,且阅读性很高,便于跟踪代码,如果你有get到,欢迎点赞关注谢谢!~~

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。