What you need to know about developing Gradle for Android

When it was less than two years after graduation, the friends around me gradually had to have cars, houses, girlfriends, and their own shows on weekends, and I felt that many beautiful things were out of my reach, and I couldn’t find anything to make me happy. very bad. But at the same time, I feel that precipitation requires time, hard work, patience and self-discipline, so I always believe that difficult times will always pass, welcome my good life will always come, and shout to myself: "Come on!".

Well, the complaints are over. I recently read the book "The Definitive Guide to Android Gradle". Although the content written in the book may be relatively simple, it should be more than enough for Android developers. So, today I intend to combine the knowledge in the book and my own development project as an example to summarize a basic point of knowledge about Gradle.

Getting to know Gradle

Gradle is a project automation build tool based on Apache Ant and Apache Maven concepts. It uses a Groovy-based domain-specific language to declare project settings, rather than traditional XML. Currently its supported languages ​​are limited to Java, Groovy and Scala, with plans to support more languages ​​in the future.

No matter how you look at it, the above is a very official explanation, which is a nightmare explanation for beginners (including the previous me). Then I will explain my understanding in simple language.

Gradle is the management of the project, helping us do dependencies, packaging, deployment, release, and difference management of various channels. For example, if I am a young master who does big things and can't handle so many small things, then Gradle is a caring secretary or housekeeper, who helps us do all the little things, so that we can play with peace of mind. Code, and other things can be handed over to the housekeeper.

Then some people will ask, since the work can be handed over to him, why do we need to understand it. I think we want the steward to do things and give our orders. We must know these orders and the steward's preferences in order to get along with him harmoniously. Otherwise, if you don't know his temperament and give wrong orders, the consequences will be very serious.

In the previous internship, I still used eclipse, that is, importing a downloaded module from the Internet requires step-by-step import. But since using Android Studio, Gradle has been very considerate to help me complete this complicated work, and often only need to add a sentence, which is amazing, I thought so at the time, and we will talk about this below.

analyze

Below I will use Gradle used in my project to slowly analyze:

We see that each Module will have a corresponding Gradle file, and there is also a Gradle file of the main Project to manage the global. Let's first look at the file called gradle-wrapper.properties:

gradle-wrapper

Wrapper is a layer of packaging for Gradle, which is convenient for unifying the version number of Gradle builds during team development, so that everyone can build with a unified Gradle version.

The picture we see above is that Gradle provides the built-in Wrapper task to help us automatically generate the directory files required by Wrapper. Let's take a look at the automatically generated files in our Android project

Finally, we know that these automatically generated files were originally created by Gradle Wrapper.

Then let's take a look at the role of the gradle-wrapper.properties file

See the various properties in the project, let's take a look at the role of each property
In fact, what we are most concerned about is the attribute distributionUrl, which is the path to download Gradle, and the things it downloads will appear in the following folders
As you can see, this folder contains the Gradle versions you downloaded for each version.

When I was a beginner, I always encountered a problem, which is the following picture:

When importing a project, it always stays in this interface, why is this? In fact, the reason is very simple. It is caused by the inconsistency between the Gradle version of your commonly used project and the Gradle version of the newly imported project. How to solve it? I myself did this:

  1. When the network speed is good or when the Internet is scientifically surfing the Internet, it will download by itself, but the download time may be long or short, which cannot be guaranteed.
  2. When you are limited in network speed in the company, of course, it is also the most commonly used by me. It is to replace the gradle-wrapper.properties file of your recently used project with the file you want to import into the project. Basically, I solved it like this, of course. Occasionally, the replacement error will be encountered, but it is relatively rare.

settings.gradle

Let's talk about the settings.gradle file, which is actually used for initialization and configuration of the project tree, and is placed in the root project directory.

Most of the settings files are used to configure self-engineering. Many projects in Gradle are represented by the project tree, which is equivalent to the concept of Project and Module we saw in Android Studio. The root project is equivalent to the Project of Android Studio. A root project can have many self-projects, that is, many Modules, which corresponds to the concept of Module defined by Android Studio.

We can see that we have added 7 modules to this project, one-to-one correspondence, if your project adds project dependencies, it will appear in this file.

Well, after we finish the settings.gradle file, we will slowly enter the other files, but first we have to explain what Groovy is:

Groovy

Groovy is a dynamic language based on the JVM virtual machine. Its syntax is very similar to Java. There is basically no obstacle to learning Groovy from Java. Groovy is fully compatible with Java, and on this basis it adds many dynamic types and flexible features, such as support for security and DSL. It can be said that it is a very flexible dynamic scripting language.

At first I always confused Gradle and Groovy, now I always figure out their relationship. Gradle is like a software, and Groovy is the language in which this software is written, it is very simple and clear. The content we talk about below is all written in Groovy grammar, but I will not popularize this knowledge point for the time being. Interested friends can learn more about Groovy grammar.

build.gradle(Project)

Let's talk about the main build.gradle file:

Here, we divide it into four labels:

1.buildscript

The declarations in the buildscript are the resources that the gradle script itself needs to use. The resources that can be declared include dependencies, third-party plugins, maven repository addresses, etc.

2.ext

ext is a custom attribute. Now many people like to use ext to centrally manage all the information about the version in another newly created gradle file. Let me introduce how ext is used:

  1. First, we create two new files, called build.gradle and version.gradle

  1. Then put the corresponding code in the two files respectively

  1. Finally, move the Terminal of Android Studio to the corresponding folder to run the task.

We can surprisingly find that when we enter the sentence apply from: 'version.gradle' in the build.gradle file, we can read the ext information in the file.

Now in the project, I also manage the version numbers of all third-party plug-ins in a unified way, and interested friends can also try it.

3.repositories

As the name implies, it means warehouse, and jcenter(), maven() and google() are platforms for hosting third-party plugins

4.dependencies

Of course, it is not enough to configure the repository. We also need to configure the dependencies that need to be configured on the classpath in the configuration in dependencies{}. Because this dependency is in buildscript{}, it represents the plugin required by Gradle.

Let's take a look at another part of the code of build.gradle (Project)

allprojects

The repositories in the allprojects block are used for multi-project builds to provide common required dependencies for all projects. Subprojects can configure their own repositories to obtain their own unique dependencies.

Strange, some people will ask why the content in buildscript and allprojects in the same build.gradle (Project) file is basically the same, and what is the difference between them?

The role and difference between buildscript and allprojects

The statement in the buildscript is the resource that the gradle script itself needs to use, that is to say, it is the resource that the housekeeper needs, and it has nothing to do with you, the young master. But allprojects declares the resources that all your modules need to use, that is to say, if each of your modules needs to use the same third library, you can declare it in allprojects. This explanation should make it clear.

Well, let's talk about the last piece of code in the build.gradle (Project) file

When running gradle clean, execute the tasks defined here. This task is inherited from Delete, which deletes the build directory in the root directory. Equivalent to executing Delete.delete(rootProject.buildDir). In fact, the execution of this task can delete the generated Build file, which is the same as the clean of Android Studio.

build.gradle(Module)

After talking about the project's build file, let's talk about the last file with the most content.

apply plugin

First of all, let's talk about apply plugin: '×××'

This is called the introduction of Gradle plugins, and Gradle plugins are roughly divided into two types:

  1. apply plugin : '×××': called binary plugin, binary plugins are generally packaged in a jar and released independently, such as our custom plugins, we can also specify plugin id for them when republishing, this plugin id is preferably a fully qualified name, like your package name;
  2. apply from : '×××': It is called an application script plug-in. In fact, this is not a plug-in, it is just a script. The application script plug-in actually loads the script. Unlike the binary plug-in, it uses the from keyword. The script file that follows it can be local or exist on the network. If it is a network Use HTTP URL. Although it is not a real plug-in, its role cannot be ignored. It is the basis for modularizing script files, and we can divide huge script files into blocks and sections. One by one shared files with clear responsibilities, and then use apply from to reference them. For example, we can put commonly used functions in a Utils.gradle script for reference by other script files. In the example, we put the version name and version number of the App in a script file, which is clear, simple, convenient, and fast. We can also use automation to automatically process the file and generate a version.

Talk about the role of the Gradle plugin

When you apply plugins to your project, plugins extend the functionality of your project and help you do many things during the build process of your project. 1. You can add tasks to your project to help you complete some tasks, such as testing, compiling, and packaging. 2. You can add dependency configurations to your project, and we can configure the dependencies our project needs during the build process through them. For example, the third-party libraries we depend on when compiling, etc. 3. You can add new extended attributes, methods, etc. to the existing object types in the project, so that you can use them to help us configure and optimize the build. For example, the android{} configuration block is an extension added by the Android Gradle plugin for the Project object . 4. You can make some conventions on the project. For example, after applying the Java plug-in, it is agreed that the src/main/java directory is our source code storage location, and the Java source code files in this directory are also compiled when compiling.

Then we say 'com.android.application'

The classification of Android Gradle plugins is actually classified according to the properties of the Android project. There are three types of projects in Andriod, one is the App application project, which can generate a runnable apk application: the other is the Library library project, which can generate AAR packages for public use by other App projects, just like our Jar , but it contains information such as Android resources and is a special Jar package; the last category is the Test test project, which is used to unit test the App project or the Library library project.

  1. App plugin id: com.android.application.
  2. Library插件id:com.android.library.
  3. Test plugin id: com.android.test.

Generally, only one App plug-in is set for a project, and the module is generally set as a Library plug-in.

android{}

It is an extension type provided by the Android plugin, which allows us to customize the Android Gradle project and is the only entry for the Android Gradle project configuration.

compileSdkVersion

Is the version of the Android SDK that the compilation depends on, here is the API Level.

buildToolsVersion

is the version of the build tool used to build this Android project.

defaultConfig{}

defaultConfig is the default configuration, which is a ProductFlavor. ProductFlavor allows us to generate multiple different apk packages at the same time according to different situations.

applicationId

Configure our package name. The package name is the unique identifier of the app. In fact, it can be different from the package in the AndroidManifest, and there is no direct relationship between them.

Package refers to the path in the code directory; applicationId refers to the unique identifier of the app released to the outside world, which will be used when signing, applying for third-party libraries, and publishing.

minSdkVersion

It is the api level of the supported Android system, here is 15, which means that models lower than Android 15 cannot use this app.

targetSdkVersion

Indicate which Android version we are developing on, here is 22.

versionCode

Indicates the internal version number of our app application, which is generally used to control the app upgrade. Of course, the bugly automatic upgrade I am using can accept the upgrade push based on this.

versionName

Indicates the version name of our app application, which is usually written on the app to tell users when it is released, so that when you fix a bug and update the version, but others find out why your bug is still there, you can do it at this time. Confidently tell him to look at the version number of the app. (Personal experience of being able to calmly deal with the torment)

multiDexEnabled

Used to configure whether this BuildType enables the function of automatically splitting multiple Dex. Generally, there are too many codes in the program, and there are more than 65535 methods.

ndk{}

Multi-platform compilation, used when generating so packages, including four platforms 'armeabi', 'x86', 'armeabi-v7a', 'mips'. Generally, when using the SDK provided by a third party, the so library may be attached.

sourceSets

Source code set is an abstract concept used by Java plug-ins to describe and manage source code and resources. It is a collection of Java source code files and resource files. We can change the Java directory or resource directory of the source set through sourceSets.

For example, like the picture above, I told Gradle that my storage path for the jni so package is in app/libs through sourceSets, and asked him to find it when compiling.

name:build type的名字

applicationIdSuffix:应用id后缀

versionNameSuffix:版本名称后缀

debuggable:是否生成一个debug的apk

minifyEnabled:是否混淆

proguardFiles:混淆文件

signingConfig:签名配置

manifestPlaceholders:清单占位符

shrinkResources:是否去除未利用的资源,默认false,表示不去除。

zipAlignEnable:是否使用zipalign工具压缩。

multiDexEnabled:是否拆成多个Dex

multiDexKeepFile:指定文本文件编译进主Dex文件中

multiDexKeepProguard:指定混淆文件编译进主Dex文件中

buildType

Build type. In the Android Gradle project, it has built two build types: debug and release. The main difference between the two modes is whether it can be debugged on the device and the signature is different. Other code and file resources are the same. of. Generally used for code obfuscation, and the specified obfuscation file is in the directory below, minifyEnabled=true will enable obfuscation:

signingConfigs

Signature configuration, an app can only be published, installed, and used after it is signed. Signature is a way to protect the app and mark the uniqueness of the app. If the app is maliciously deleted, the signature will be different, and it cannot be upgraded and installed, which protects our app to a certain extent. And signingConfigs is very convenient to provide us with the configuration of this signature. storeFile signature file, password of the storePassword signature certificate file, storeType signature certificate type, key alias in the keyAlias ​​signature certificate, and password of the changed key in the keyPassword signature certificate.

By default, the signature of the debug mode has been configured, and the debug certificate automatically generated by the Android SDK is used. It is generally located in $HOME/.android/debug.keystore, and its key and password are already known. Generally, We do not need to configure the signature information of debug mode separately.

productFlavors

In my opinion, it is Gradle's multi-channel packaging. You can define different variables in different packages to achieve your own customized version requirements.

manifestPlaceholders

A placeholder, through which we can dynamically configure some contents of the AndroidManifest file, such as the name of the app:

Looking at the picture above, we can find that after we define manifestPlaceholders = [APP_NAME: "(test)"] in productFlavors, add "${APP_NAME}" to the label of AndroidManifest, we can control the type of each package. The name is what we want to have different names, for example, the package name of the test server and the production server should be different.

buildConfigField

It is a function of the BuildConfig file, and the BuildConfig class is generated by the Android Gradle build script after compilation. And buildConfigField is one of the custom function variables. Looking at the figure below, we define three constants:

We can see the three variables we declared in the BuildConfig file

Then we can use these variables in the code to control different versions of the code:
By adding an if in this way, we can easily control the problem of paying for the test and production versions, and we no longer have to manually change it. Then the problem comes, how can I choose a different version? See the picture below :

If you are Android Studio, find Build Variants and select the version you are currently compiling.

flavorDimensions

As the name implies, it is a dimension. After Gradle 3.0, the variables that use flavorDimensions must be defined in defaultConfig{} before they can be used, otherwise an error will be reported:

Error:All flavors must now belong to a named flavor dimension.
The flavor 'flavor_name' is not assigned to a flavor dimension.

In this way, we can form different applicationId and versionName in different packages.

dexOptions{}

We know that after the Java source code in Android is compiled into class bytecode, it is optimized by the dx command into a DEX file executable by the Android virtual machine when it is packaged into apk. The DEX file is relatively compact, and Android took great pains to make this DEX format in order to make our program run faster on the Android platform. For the process and processing of generating DEX files, the Android Gradle plugin has handled it for us. The Android Gradle plugin will call the dx command in the SDK for processing. However, sometimes you may encounter an error indicating insufficient memory. The general error is java, lang.OutOfMemoryError: GC overhead limit exceeded. Why does it indicate insufficient memory? In fact, this dx command is just a script. It calls the dx.jar library written in Java, which is processed by the Java program, so when the memory is insufficient, we will see this Java exception message. By default, the memory allocated to dx It is a G8, which is 1024MB.

So we only need to set the memory larger to solve this problem. My project in the picture above sets the memory to 4g.

dependencies{}

This is probably the one we use the most.

  1. First of all, the first sentence compile fileTree(include: ['*.jar'], dir: 'libs') , so that after configuration, the extension named jar in the local libs folder will be depended on, which is very convenient.
  2. If you want to import a local module, you need to use compile project('×××') .
  3. If we want to introduce the dependencies in the online warehouse, we need to write the compile group like this: 'com.squareup.okhttp3',name:'okhttp',version:'3.0.1' , of course, this is the most complete version, the abbreviation is the group , name, and version are removed, and then separated by ":". compile 'com.squareup.okhttp3:okhttp:3.0.1'
    But after gradle3.0, the dependencies in build.gradle default to implementation, not the previous compile. In addition, there are dependency directive APIs. So let's talk about:

Differences between implementation and api dependencies in gradle 3.0:

In fact, the api is no different from the previous compile. It is not wrong to change all the compile to api; and the implementation instruction dependency will not be passed, that is to say, the currently referenced third-party library is only used in this module, and other modules need Re-adding dependencies can be used. The following two diagrams are used to illustrate:

I'm sure anyone who's seen the picture will understand. Well, the content of this issue is almost written. If there are any mistakes or omissions in the above content, please let me know. Although I only talked about a few gradle files, I feel that some niche content has not been written yet. If there is a need later, I will make it up. That's all for today, see you next time!

my brief book

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325966135&siteId=291194637