I mind if you don’t mind.

preface

Brother Han asked for the reconstruction of the opportunity, just a good review of the previous forgotten/neglected knowledge points.

Note the path to Gradle optimization:

  • Android refactoring | Unified management of Gradle dependencies
  • Android | modularity explore extraction of BASIC to simplify submodule redundancy

The general direction or ultimate goal can be condensed as follows:

  • One reference, full text (project) use, avoid team collaboration to introduce repeated dependencies;
  • Built-in dependency update prompt;
  • Supports common operations such as jump.

Most importantly, it is still easy to maintain.

Gradle basic_depend. Gradle is better today than it was yesterday, but it is still not very satisfying.

Although ext is officially recommended by Google at present, and some mainstream libraries also use this way, the actual use of personal or some inconvenient. You don’t support jumps, you don’t support updates, you know, you always want more.

After reviewing multiple documents, prepare to optimize/upgrade a wave again and continue to keep Han in the loop.

buildSrc

Google translated the official description as follows:

Complex build logic often lends itself well to encapsulation as custom tasks or binary plug-ins. Custom tasks and plug-in implementations should not exist in build scripts. BuildSrc makes it very easy to use as long as you don’t need to share code across multiple independent projects.

The directory buildSrc is treated as an included build. Once the directory is found, Gradle automatically compiles and tests this code and places it in the classpath of the build script. For multi-project builds, there can only be one buildSrc directory, which must be in the root project directory. BuildSrc should be preferable to scripting plug-ins because it is easier to maintain, refactor, and test code.

BuildSrc uses the same source code conventions that apply to Java and Groovy projects. It also provides direct access to the Gradle API. Other dependencies can declare buildSrc under a dedicated build.gradle.

Thinking for a long time, the personal summary is simple:

  • BuildSrc exists at Gradle compile time;
  • BuildSrc also supports (individual projects) sharing code, such as multiple Modules in a project that can be called directly.

BuildSrc practice

Describe the operation steps:

  • Create a buildSrc directory in the project root directory, then create a new build.gradle. KTS file.
  • Create SRC directory, and the corresponding management version file;
  • The substitution uses the original dependency directly

Build.gradle. KTS contains the following contents:

Plugins {' kotlin-dsl '} repositories {jCenter ()} /** * Disable the Test report (Gradle default will automatically create the Test report) * / tasks in withType < Test > {reports. HTML. IsEnabled = false reports. JunitXml. IsEnabled = false} /** * isFork: runs the compiler as a separate process. * This process is reused during build time, so fork overhead is minimal. The benefit of forking is that memory-intensive compilation takes place in a different process, resulting in significantly less garbage collection in the main Gradle daemon. * Less garbage collection in daemons means that Gradle's infrastructure can run faster, especially if you're still using parallel. * * isIncremental: incremental compilation. Gradle can analyze dependencies up to the level of a single class in order to recompile only the classes affected by the change. Since Gradle 4.10, incremental compilation is the default. */ tasks.withType<JavaCompile> {options.isFork = true options.isIncremental = true} /** * Disable warnings about using the experimental Kotlin compiler feature */  kotlinDslPluginOptions { experimentalWarning.set(false) }Copy the code

Dependencies. Kt is a versioning file that I define as follows:

@file:Suppress("SpellCheckingInspection") /** * @author HLQ_Struggle * @date 2020/7/27 * @desc Unified management */ / Unified management of project version information Object Versions {// Build Config const val compileSDK = 29 // Build SDK version const val buildTools = "29.0.3" // Gradle Compile project tool Version const val minSDK = 23 // lowest Compatible Android Version const val targetSDK = 29 // highest compatible Android Version // App Version const val AppVersionCode = 1 // Current version number const val appVersionName = "1.0" // current version information // Plugins const val androidGradlePlugin = // const val Kotlin = "1.3.72" const val Kotlin = "1.3.5" // Support Lib const val Support = Const val junit = "4.12" const val appCompat = "1.1.0" const val constrainLayout = "1.1.3" // Testing const val junit = "4.12" const val ExtJunit = "1.1.1" const val espresso = "3.2.0"} // Gradle const val androidGradle = "com.android.tools.build:gradle:${Versions.androidGradlePlugin}" // Kotlin const val kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${Versions.kotlin}" const val kotlinGradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlin}" const val kotlinxCoroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.kotlinxCoroutines}" // Testing const val junit = "junit:junit:${Versions.junit}" const val extJunit = "androidx.test.ext:junit:${Versions.extJunit}" const val espresso =  "androidx.test.espresso:espresso-core:${Versions.espresso}" // Android const val appcompat = "Androidx. Appcompat: appcompat: ${Versions. Appcompat}" const val coreKtx = "androidx. Core: the core - KTX: 1.2.0" const val ConstraintLayout = "androidx. ConstraintLayout: constraintLayout: ${Versions. ConstrainLayout}" / / Fix: About 64 k reference limit const val multiDex = "androidx. MultiDex: multiDex: 2.0.1" / / Jetpack const val viewPager2 = "Androidx. Viewpager2: viewpager2:1.0.0" / /... }Copy the code

Here are two chestnuts, how to use them:

  • How to use build in root directory:

Obtain the corresponding properties by referring to the group name defined in the Dependencies file, as shown below:

buildscript {
    // ...
    dependencies {
        classpath Deps.androidGradle

        classpath Deps.kotlinGradlePlugin

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
// ...
Copy the code
  • How to use builds in other Module directories:

Similarly, of course, the whole corresponding grouping can be directly poured into the corresponding attributes, such as:

Import static Versions.* import static Versions.* apply plugin: import static Versions. 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'Kotlin-android-extensions' Android {// you can use the property compileSdkVersion compileSDK buildToolsVersion buildTools defaultConfig { minSdkVersion minSDK targetSdkVersion targetSDK versionCode appVersionCode versionName appVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'consumer-rules.pro' } // ... } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // Same here, Implementation kotlinStdLib Implementation AppCompat Implementation coreKtx API 'com. Google. Android. Material: material: 1.2.0' testImplementation junit androidTestImplementation extJunit AndroidTestImplementation espresso API MMKV API 'com. Reality. Android: Lottie: 3.4.1 track'}Copy the code

This method has several good characteristics as follows:

  • Support jump;
  • Support intelligent prompt;
  • Gradle builds in and feels wet and high

But what about the key update tips?

Ummm, not open.

Add a GIF to illustrate it

To write buildSrc manually, note:

  • Directory structure: for example: buildSrc/ SRC /main/kotlin (Java)
  • Add jcenter() in build.gradle.kts, otherwise kotlin-DSL load fails

(2020/09/15)

I found the description of the refreshVersions online, so I think it is quite suitable.

The general advantages are as follows:

  • Centrally manage dependencies
  • Prompt dependency upgrades at minimal cost

The operation steps are as follows:

Step 1: Modify settings.gradle

// settings.gradle.kts import de.fayard.refreshVersions.RefreshVersionsSetup // Here you might have some pluginManagement block: pluginManagement { //... } buildscript { repositories { gradlePluginPortal() } Dependencies. The classpath (" DE. Fayard. RefreshVersions: refreshVersions: 0.9.5 ")} rootProject. Name = 'Your Android Project Name' include ':app' include ':helper' include ':weight' // include other module RefreshVersionsSetup.bootstrap(settings)Copy the code

Step 2: Run the command after the synchronization

./gradlew migrateToRefreshVersionsDependenciesConstants --console=plain
Copy the code

Follow the prompts for dependency substitution:

Then generate the versions.properties file:

## suppress inspection "SpellCheckingInspection" for whole file ## suppress inspection "UnusedProperty" for whole file ## ## Dependencies and Plugin versions with their available updates ## Generated by $ ./gradlew refreshVersions ## Please, don't put extra comments in that file yet, Keeping them is not supported yet. Version. Androidx. Appcompat = 1.1.0 # # # available = 1.2.0 - alpha01 # # # Available =1.2.0-alpha02 ## # available=1.2.0-beta01 ## # available=1.2.0-rc01 ## # Available = 1.2.0-RC02 ## # available=1.2.0 ## # available=1.3.0-alpha01 ## # available=1.3.0-alpha02 Androidx.core =1.2.0 ## # available=1.3.0-alpha01 ## # available=1.3.0-alpha02 ## # available=1.3.0-beta01 ## # Available = 1.3.0-RC01 ## # available=1.3.0 ## # available=1.3.1 ## # available=1.4.0-alpha01 ## # available=1.5.0-alpha01 ## available=1.5.0-alpha02 ##...Copy the code

A bit uncomfortable is that it has some of the Android dependencies built in, and the rest are not very friendly for us to use in actual development, as shown below:

After a while of research, the actual effects of the various masks are still not satisfactory, and it would be nice if we could add a version update based on buildSrc.

BuildSrc with task (2020/09/17)

I have to say, there are a lot of nuggets, very friendly, this, sink well float light CBFG big guy taught me a trick ~

There is a link attached at the end of the article. If you are interested, you can learn by yourself

I simply summarize the big guy’s practical ideas:

  • A new version of version. gradle is used to store dependency/plug-in configurations.
  • New updateDependencies. Gradle task for the updated/plug-in synchronization groovy;
  • Just use a direct call to Groovy.

Step 1: Create the buildSrc directory in the project root directory

Step 2: Create a version. Gradle dependency/plug-in management

Big guy in the log and write very clear, here I say separately during my encounter pit, or is the key bar, let see this little partner faster start.

  • After parsing, the object Versions of Dependencies. Kt must exist as follows:
Def compileSDK = 29 / build SDK version def buildTools = "29.0.3" // Gradle build project tool version def MinSDK = 23 // minimum compatible Android version def targetSDK = 29 // maximum compatible Android version /*</version>*/ <-- must existCopy the code
  • The path between the Deps is inserted into the object Deps of Dependencies.
* / * < dep > / < - must exist / / gradlePlugin implementation "com. Android. View the build: gradle: 4.0.1" / / PermissionsDispatcher: Android 6.0 dynamic permissions management implementation "org. PermissionsDispatcher: permissionsDispatcher: 4.7.0" /*</dep>*/ <-- must existCopy the code

Note that the first part of // represents the name of the call in use, and the last part represents the description of the current dependency.

The full version. Gradle content is as follows (space is limited, remove dependencies used in some projects) :

Dependencies {/* readme * * * * Gradlew updateDependencies task * parses this file into Dependencies. Kt for unified reference * * <version> </version> * * <dep> </dep> </dep> <name> = <value> <name> <value> <name> <value> //< plugin/dependency library name > : < remarks >, This section will be parsed to determine the plug-in/dependent libraries reference name * 2, configure plugins/dependent libraries reference path to implementation as the beginning * 3, update, configuration executed after updateDependencies. Gradle updateDependencies Update task to Dependencies. Kt * * Extra: Maven Repository] * [Google 's (https://dl.google.com/dl/android/maven2/index.html) * / / * * * Version part / / * < Version > * / Def compileSDK = 29 // build SDK version def buildTools = "29.0.3" // Gradle build project tool version def minSDK = 23 // minimum compatible Android version def compileSDK = 29 // build SDK version def buildTools = "29.0.3" // Gradle build project tool version def minSDK = 23 // minimum compatible Android version def TargetSDK = 29 // highest compatible Android version /*</version>*/ /* * plugin/dependent library path part */ /*<dep>*/ // gradlePlugin implementation "Com. Android. Tools. Build: gradle: 4.0.1" / * * * Third Lib * / / / indicator: indicator implementation "Com. HLQ - dictated: indicator: 1.0.0" / / permissionsDispatcher: Android 6.0 dynamic permissions management implementation "Org. Permissionsdispatcher: permissionsdispatcher: 4.7.0" / / permissionsDispatcherProcessor implementation "Org. Permissionsdispatcher: permissionsdispatcher - processor: 4.7.0" / / MMKV: high performance general key - value based on mmap component implementation "Com. Tencent: MMKV -static: 1.1.0" / * < / dep > * /}Copy the code

Step 3: (copy) new bosses provide updateDependencies. Gradle

The relevant log is written very clearly, we can read it carefully.

The following sections focus on synchronizing dependencies/plug-ins that are written in version according to rules in Groovy.

/ * * * will versions. Gradle/xVersion gradle configured in the version information generated in the SRC/main/groovy/Dependencies. Groovy * perform this task method: * method 1: * New: In the Gradle Task list panel, click the 'Execute Gradle Task' button, type '-p buildSrc updateDependencies' in the input box, and press Enter. * Deprecated: In the Gradle Task list panel, click the 'Run Gradle Task' button. In the 'Gradle Project' column, select the buildSrc module under 'Command ' Line, type 'updateDependencies' and click 'OK'; * New: In Terminal, type 'gradlew -p buildSrc updateDependencies' and execute * Deprecated: Enter 'gradlew updateDependencies' in Terminal and then execute method 3: * AS->Edit Configurations->Gradle by clicking '+' in the upper left corner. $projectName:buildSrc [updateDependencies] * Gradle project: $projectName/buildSrc * Tasks: UpdateDependencies * Click 'Apply' to save this configuration */ task("updateDependencies") {String inputFilePath = "versions. Gradle "String OutputFilePath = "SRC/main/groovy/Dependencies. Groovy" / / will inputFilePath statement for the Task of inputs inputs. The file (inputFilePath) / / Elsion.file (outputFilePath) doLast {file inputFile = file(inputFilePath) if (! inputFile.exists()) { return } String inputTxt = inputFile.text StringBuilder builder = new StringBuilder() /* * Append (" * version info \n").append(" */\n").append(" */\n").append("interface Versions {\n") String startFlag = "/*<version>*/" String endFlag = "/*</version>*/" int start = inputTxt.indexOf(startFlag) int end = inputTxt.indexOf(endFlag) builder.append(" ") .append(inputTxt.substring(start + startFlag.length(), End). The trim ()). Append (" \ n \ n \ n} ") analytical splicing depend on the object / * * * / builder. Append (" / * * \ n "), append (" * depend on the library path \ n "), append (" * / \ n ") .append("interface Deps {\n") startFlag = "/*<dep>*/" endFlag = "/*</dep>*/" start = inputTxt.indexOf(startFlag) end = inputTxt.indexOf(endFlag) String depsTxt = inputTxt.substring(start + startFlag.length(), end).trim() int implementationIndex int doubleSlashIndex while (true) { implementationIndex = depsTxt.indexOf("implementation") if (implementationIndex == -1) { break } doubleSlashIndex = depsTxt.lastIndexOf("//", implementationIndex) String namePart String name while (true) { namePart = depsTxt.substring(doubleSlashIndex + 2, implementationIndex) name = namePart.split(":")[0].trim() if (! name.contains("/")) { break } doubleSlashIndex = depsTxt.lastIndexOf("//", doubleSlashIndex - 2) } depsTxt = depsTxt.replaceFirst("implementation", String.format("def %s =", name)) } builder.append(" ") .append(depsTxt) .append("\n}\n") String resultTxt = builder.toString() file(outputFilePath).withPrintWriter("utf-8", { writer -> writer.print(resultTxt) writer.flush() }) } }Copy the code

Create the final build.gradle

apply plugin: 'groovy'
apply from: 'updateDependencies.gradle'
Copy the code

Step 5: Run the synchronization command

Of course, big guy provided three kinds of programs, choose a personal habit can be.

Copy the following command in Step 3:

  • -p buildSrc updateDependencies

Notice where I draw the red line. This is a similar operation of history provided by AS. It is very convenient to record the task we used last time and save input every time.

The execution speed is still pretty fast, which then generates our Groovy file:

This file is the same as our versions. Gradle file.

Step 6: How to use it?

It’s actually pretty easy up here, just a quick example.

  • Versions use:

  • Use Deps

  • How to update and synchronize?

Thanks to the Nuggets

4. Continue to package and extract builds based on BASIC

After the basic improvement, silently hurried or a little uncomfortable place, such as:

The current structure is one APP corresponding to other modules, and each module has some same but different content. If it is adjusted in the later stage, do I need to modify it one by one? Isn’t it just another round of disdain from the chicken boss?

Think about it. Think about it.

Gradle. Apply to each module. This saves a lot of code.

Please look at the basic.gradle package:

apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android {// Specify the API level compileSdkVersion Versions.compileSDK // specify the SDK tool version to be used when building the project, Manual configuration is not required after Android Studio 3.0. BuildToolsVersion versions. buildTools // Specifies the default value defaultConfig {minSdkVersion versions.minsdk for the version property of the Android plug-in that applies to all builds TargetSdkVersion Versions. TargetSDK versionCode 1 versionName "1.0" // Retain only the Chinese resource resConfigs "zh" // Enable the multi-dex file MultiDexEnabled true NDK {// Set the supported SO library framework abiFilters "armeabi", "X86} testInstrumentationRunner androidx. Test. Runner. AndroidJUnitRunner"} / / configure Java compilation (level of encoding format, compile and produce bytecode version) compileOptions { encoding = 'utf-8' sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility VERSION_1_8} kotlinOptions {jvmTarget = JavIstan.version_1_8.toString ()} // Enable view binding compatible with Gradle 4.x and later BuildFeatures {dataBinding = true viewBinding = true // gradle 5.x +} lintOptions {// Continue abortOnError after exception }} /** * implementation: does not pass down, only in the current module; Dependencies {implementation fileTree(dir: 'implementation fileTree') ['*.jar']) implementation Deps.kotlinStdlibJdk7 implementation Deps.appcompat implementation Deps.coreKtx testImplementation Deps.junit androidTestImplementation Deps.extJunit androidTestImplementation Deps.espressoCore }Copy the code

My app build.gradle:

apply plugin: 'com.android.application' apply from: ".. DefaultConfig {applicationId "com.pwccn.fAdvisor"} // Specifies the default value for the version property of the Android plugin for all builds BuildTypes {debug {// Log controller - Output Log buildConfigField "Boolean ", "LOG_DEBUG", EnableCrashlytics = false // Disable automatic build ID ext.alwaysUpdateBuildId = false // ShrinkResources false // minifyEnabled false // zipAlign optimization zipAlignEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'} release {// Log controller - disallow logging buildConfigField "Boolean ", "LOG_DEBUG", "False" // Enable shrinkResources true // Enable code shrinking minifyEnabled true // Enable zipAlign optimization zipAlignEnabled true proguardFiles GetDefaultProguardFile ('proguard-android-optimize.txt'), 'proguard-rules.pro'}}} /** * implementation: Does not pass down, only on the current module; API: Passing down, Use */ dependencies {implementation demos.legacySupportV4 implementation demos.lifecycleExtensions Implementation Deps. LifecycleViewModelKtx / / modular parts import / / helper implementation project (path: ':helper') // weight implementation Project (Path: ':weight') }Copy the code

My Helper Module:

apply plugin: 'com.android.library' apply from:".. /basic.gradle" dependencies { api Deps.constraintLayout api Deps.xPopup // helper implementation project(path: ':helper') }Copy the code

Ummm, Basic with buildSrc, really feels comfortable.

The combination of the third and the fourth is highly recommended. It really makes you feel great

The resources

  • Configure project global properties
  • Use buildSrc to abstract imperative logic
  • refreshVersions
  • Unified management of plugin and dependency library information ->buildSrc
  • maven.google.com
  • BuildSrcDemo