Android Gradle learning series (8)-Detailed explanation of other Gradle modules and actual combat with Plugin

1. GradleOther modules

1.1 Settingsclass

This Settingclass may not have seen before us, but settings.gradlewe have seen many times
Insert picture description here
that this setttings.gradleand our Settings.javaso what does it matter? We recall our previous Project, it contains a default file name is called build.gradle, that is, each of us Projectby this initialization is build.gradleto initialize the Projectentity object, here we are this settings.gradleand Settingsclass is the same, by settings.gradlethe configuration to Settingsinitialize, their core function is to decide which projects need to be gradletreated
Insert picture description here
Settingslike the most important is this inclueMethod, through this method we can introduce our new project. This settings,gradleis very simple for our developers, but it is not simple in this province. It takes up gradleone third of the entire life cycle, which is the `initialization phase.

1.2 SourceSetClass

Insert picture description here
We know that our engineers src/main/javawrite source code in the directory and finally compile it into bytecode files, and src/main/resour resource files are stored in the directory. This is our gradledefault agreement, which is also our sourceSetbiggest role. Manage our source code, resources, libraries, etc. Where to store it. Since there is a default, can we change it? The answer is yes. First AndroidSourceSet, let's take a look at the source code of this class and see what we can change

public interface AndroidSourceSet {



    /**
     * Configures the Java resources for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
     * contains the java resources.
     *
     * @param configureClosure The closure to use to configure the javaResources.
     * @return this
     */
    @NonNull
    AndroidSourceSet resources(Closure configureClosure);

    
    /**
     * Configures the Java source for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet} which
     * contains the Java source.
     *
     * @param configureClosure The closure to use to configure the Java source.
     * @return this
     */
    @NonNull
    AndroidSourceSet java(Closure configureClosure);

  
    /**
     * Configures the location of the Android Manifest for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceFile} which contains the
     * manifest.
     *
     * @param configureClosure The closure to use to configure the Android Manifest.
     * @return this
     */
    @NonNull
    AndroidSourceSet manifest(Closure configureClosure);

    /**
     * Configures the location of the Android Resources for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
     * which contains the resources.
     *
     * @param configureClosure The closure to use to configure the Resources.
     * @return this
     */
    @NonNull
    AndroidSourceSet res(Closure configureClosure);

   

    /**
     * Configures the location of the Android Assets for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
     * which contains the assets.
     *
     * @param configureClosure The closure to use to configure the Assets.
     * @return this
     */
    @NonNull
    AndroidSourceSet assets(Closure configureClosure);

  

    /**
     * Configures the location of the Android AIDL source for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
     * which contains the AIDL source.
     *
     * @param configureClosure The closure to use to configure the AIDL source.
     * @return this
     */
    @NonNull
    AndroidSourceSet aidl(Closure configureClosure);

  

    /**
     * Configures the location of the Android RenderScript source for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
     * which contains the Renderscript source.
     *
     * @param configureClosure The closure to use to configure the Renderscript source.
     * @return this
     */
    @NonNull
    AndroidSourceSet renderscript(Closure configureClosure);


    /**
     * Configures the location of the Android JNI source for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
     * which contains the JNI source.
     *
     * @param configureClosure The closure to use to configure the JNI source.
     * @return this
     */
    @NonNull
    AndroidSourceSet jni(Closure configureClosure);

  

    /**
     * Configures the location of the Android JNI libs for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
     * which contains the JNI libs.
     *
     * @param configureClosure The closure to use to configure the JNI libs.
     * @return this
     */
    @NonNull
    AndroidSourceSet jniLibs(Closure configureClosure);

   

    /**
     * Configures the location of the Android shaders for this set.
     *
     * <p>The given closure is used to configure the {@link AndroidSourceDirectorySet}
     * which contains the shaders.
     *
     * @param configureClosure The closure to use to configure the shaders.
     * @return this
     */
    @NonNull
    AndroidSourceSet shaders(Closure configureClosure);

    /**
     * Sets the root of the source sets to a given path.
     *
     * All entries of the source set are located under this root directory.
     *
     * @param path the root directory.
     * @return this
     */
    @NonNull
    AndroidSourceSet setRoot(String path);
}

Here I have removed all getmethods, we only care about the settings.

The first example: modify the sofile location of our default storage. The default storage location of
our AS project is as src/main/jniLibsfollows, we want to store it in our libsdirectory

 sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }

The second example: ressubcontracting, like our javacode,
here we first create a few folders, which are at the ressame level as ours.
Insert picture description here
Here we find that the two new folders we added are resstill not the same as ours. folder, then we put these two folders to add to our sourceSetsin

sourceSets {
        main {
            res.srcDirs = ['src/main/res',
                           'src/main/res-ad',
                           'src/main/res-player']
        }
    }

After synchronization, we are watching
Insert picture description here

We can also two examples of our above code written in one sourceSetsof the
Insert picture description here

You can also get it androidoutside
Insert picture description here

2. GradleThe Pluginactual

2.1 PluginDefinition

GradleIn the package Pluginis to complete the specified function Task, as long as the project depends on a certain Plugin, you can perform Pluginall the functions in it, for example: using the Javaplug-in can be jarpackaged, using the Androidplug-in, you can generate apk、aar.

2.2 Create aPlugin

2.2.1 Create a plug-in project

  1. Create a buildSrcfolder under our project directory .
    Insert picture description here
  2. Under the buildSrcdirectory, create src/mainfolders and build.gradlefiles.
    Insert picture description here
  3. In the mainCreate the following folders groovyand resourcesfolders
    Insert picture description here
  4. In resourcescreating a META-INFfolder, and then in the META-INFcreation of a gradle-pluginsfolder.
    Insert picture description here
  5. build.gradleEnter the following script in the file:
apply plugin: 'groovy'

sourceSets {
    main {
        groovy {
            srcDir 'src/main/groovy'
        }

        resources {
            srcDir 'src/main/resources'
        }
    }
}
  1. Compile
    Insert picture description here

2.2.2 Create a plugin class

This is Javabasically the same as ours, we groovycreate a folder under our com.hfs.gradle.studyfolder, this is equivalent to our package name

Insert picture description here
We then studycreate a folder plugin class GradleStudyPlugin, pay attention, here GradleStudyPluginis a .groovysuffix, not .javabecause we are creating a plug-in, so we need to take this class implements Gradlethe interface provided
Insert picture description here

2.2.3 Specifying the plugin entrance

We next want to declare how others use our plug-in, so where do we declare it? We need to META-INF.gradle-pluginscreate a propertiesfile in the directory first , propertieswe generally use the package name of the plug-in class to define
Insert picture description here
the content inside is very simple, just a sentence

Insert picture description here

2.2.4 Using custom plugins

The next step is to use it, it is very simple, apply plugin:plus the package name of our plugin, for example, our appproject needs to introduce this plugin, open appthe build.gradlefile, plus this sentence
Insert picture description here
Insert picture description here

2.2.5 Creating extended attributes

1. Create a class entity for receiving the gradle中parameters, similar to our Javaclasses, but to .groovythe suffix

package com.hfs.gradle.study
/**
 * 与自定义Plugin进行参数传递
 */
class ReleaseInfoExtension {

    /**
     * 版本号
     */
    String versionCode
    /**
     * 版本名字
     */
    String versionName
    /**
     * 版本信息
     */
    String versionInfo
    /**
     * 文件路径
     */
    String fileName

    ReleaseInfoExtension() {

    }

    @Override
    String toString() {
        """| versionCode = ${versionCode}
           | versionName = ${versionName}
           | versionInfo = ${versionInfo}
           | fileName = ${fileName}
        """.stripMargin()
    }
}

2. Create in a custom plugin

/**
 * 自定义Plugin
 */
class GradleStudyPlugin implements Plugin<Project> {

    /**
     * 唯一需要实现的就是这个方法,参数就是引入了当前插件的Project对象
     * @param project
     */
    @Override
    void apply(Project project) {
        //创建扩展属性
        project.extensions.create('hfsReleaseInfo', ReleaseInfoExtension)
    }

}

In app to build.gradlebe used in
Insert picture description here
the next
Insert picture description here

2.2.6 Create Extended Task

package com.hfs.gradle.study

import groovy.xml.MarkupBuilder
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction

/**
 * 自定义Task,实现维护版本信息功能
 */
class ReleaseInfoTask extends DefaultTask {

    ReleaseInfoTask() {
        // 指定分组
        group = 'hfs'
        //说明信息
        description = 'update the release info'
    }


    /**
     * 使用TaskAction注解,可以让方法在gradle的执行阶段去执行。
     * doFirst其实就是在外部为@TaskAction的最前面添加执行逻辑。
     * 而doLast则是在外部为@TaskAction的最后面添加执行逻辑。
     */
    @TaskAction
    void doAction() {
        updateInfo()
    }

    //真正的将Extension类中的信息呢,写入指定文件中
    private void updateInfo() {
        //获取将要写入的信息
        String versionCodeMsg = project.extensions.
                hfsReleaseInfo.versionCode
        String versionNameMsg = project.extensions.
                hfsReleaseInfo.versionName
        String versionInfoMsg = project.extensions.
                hfsReleaseInfo.versionInfo
        String fileName = project.extensions.
                hfsReleaseInfo.fileName
        def file = project.file(fileName)
        if (file != null && !file.exists()) {
            file.createNewFile()
        }
        //将实体对象写入到xml文件中
        def sw = new StringWriter()
        def xmlBuilder = new MarkupBuilder(sw)
        if (file.text != null && file.text.size() <= 0) {
            //没有内容
            xmlBuilder.releases {
                release {
                    versionCode(versionCodeMsg)
                    versionName(versionNameMsg)
                    versionInfo(versionInfoMsg)
                }
            }
            //直接写入
            file.withWriter { writer -> writer.append(sw.toString())
            }
        } else {
            //已有其它版本内容
            xmlBuilder.release {
                versionCode(versionCodeMsg)
                versionName(versionNameMsg)
                versionInfo(versionInfoMsg)
            }
            //插入到最后一行前面
            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))
                    }
                }
            }
        }

    }
}

Like custom attributes, they need to be created in a custom plugin

/**
 * 自定义Plugin
 */
class GradleStudyPlugin implements Plugin<Project> {

    /**
     * 唯一需要实现的就是这个方法,参数就是引入了当前插件的Project对象
     * @param project
     */
    @Override
    void apply(Project project) {
        //创建扩展属性
        project.extensions.create('hfsReleaseInfo', ReleaseInfoExtension)

        //创建Task
        project.tasks.create('hfsReleaseInfoTask', ReleaseInfoTask)
    }

}

Under synchronization, you will gradlesee this intask
Insert picture description here

Execute thistask
Insert picture description here

3. androidPlug-in gradleextension

I will take an app from a project to build.gradlesee what is in the androidclosure. What
Insert picture description here
can be configured here? Mainly look at BaseExtensionthis class

BaseExtension official specific explanation
Insert picture description here
So where are the androidintroductions task? Mainly look at BaseVariantthis class

/**
 * A Build variant and all its public data. This is the base class for items common to apps,
 * test apps, and libraries
 */
public interface BaseVariant {
    /**
     * Returns the name of the variant. Guaranteed to be unique.
     */
    @NonNull
    String getName();
    /**
     * Returns a description for the build variant.
     */
    @NonNull
    String getDescription();
    /**
     * Returns a subfolder name for the variant. Guaranteed to be unique.
     *
     * This is usually a mix of build type and flavor(s) (if applicable).
     * For instance this could be:
     * "debug"
     * "debug/myflavor"
     * "release/Flavor1Flavor2"
     */
    @NonNull
    String getDirName();
    /**
     * Returns the base name for the output of the variant. Guaranteed to be unique.
     */
    @NonNull
    String getBaseName();
    /**
     * Returns the output file for this build variants. Depending on the configuration, this could
     * be an apk (regular and test project) or a bundled library (library project).
     *
     * If it's an apk, it could be signed, or not; zip-aligned, or not.
     */
    @NonNull
    File getOutputFile();
    void setOutputFile(@NonNull File outputFile);
    /**
     * Returns the Manifest processing task.
     */
    @NonNull
    ProcessManifest getProcessManifest();
    /**
     * Returns the AIDL compilation task.
     */
    @NonNull
    AidlCompile getAidlCompile();
    /**
     * Returns the Renderscript compilation task.
     */
    @NonNull
    RenderscriptCompile getRenderscriptCompile();
    /**
     * Returns the resource merging task.
     */
    @Nullable
    MergeResources getMergeResources();
    /**
     * Returns the asset merging task.
     */
    @Nullable
    MergeAssets getMergeAssets();
    /**
     * Returns the Android Resources processing task.
     */
    @NonNull
    ProcessAndroidResources getProcessResources();
    /**
     * Returns the BuildConfig generation task.
     */
    @Nullable
    GenerateBuildConfig getGenerateBuildConfig();
    /**
     * Returns the Java Compilation task.
     */
    @NonNull
    JavaCompile getJavaCompile();
    /**
     * Returns the Java resource processing task.
     */
    @NonNull
    Copy getProcessJavaResources();
    /**
     * Returns the assemble task.
     */
    @Nullable
    Task getAssemble();
}

Documentation The
above document has a detailed introduction

Next, let's practice the custom apkoutput path and name

this.afterEvaluate {
  this.android.applicationVariants.all { variant ->
    def output = variant.outpus.first() 
    def apkName = "app-${variant.baseName}-${variant.versionName}.apk"
    output.outputFile = new File(output.outputFile.parent, apkName)
  }
}

Published 87 original articles · Like 319 · Visit 1.49 million +

Guess you like

Origin blog.csdn.net/Greathfs/article/details/102809390