For the first time to read dry goods, you can pay attention to my public number: program ape cultivation center

This topic is very difficult to answer today. The content of componentization category is related to EventBus. However, by using the form of Gradle plug-in, we can simply say that the message type of EventBus is automatically compiled under componentization.

The EventBus library is a popular way to define a message type. If you have used EventBus, you need to define a message type.

In componentized projects, message types are defined in common modules so that they can be passed across components. This works, but there are two drawbacks:

  • The need to manually change the public library, which brings certain risks to the public library, more and more code added to the public library will be difficult to maintain later, the componentization boundary is difficult to define, the public library needs to be stable
  • The split between the components is not very clean, if the modification of component communication content at the same time need to modify the content in the public library, if the development of component module A can not see the source code in the public library? Just catch blind

So our goal is that the types of messages a component needs to receive or send are defined directly within its own component scope, without having to modify any code outside of the component. The less you change, the less bugs you will have, and the sooner you can leave work, right?

Results 1.

Let’s take a look at the overall project structure

Divided into app/event/modulea moduleb four parts Send a message in the app, a and b can be received, the contents of the a and b the same, just looking at the code in under a a ADisplay used to display the received message, a ModuleaMsg used to define the message type.

Take a look at the code in ADisplay and accept the ModuleaMsg message:

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(ModuleaMsg msg) {
      System.out.println("Module A received msg = " + msg.msg);
      EventBus.getDefault().unregister(this);
}

Copy the code

ModuleaMsg is the message type? Check in with component A, write a file that ends with.event, and write code in it

This does all the work the component needs to do, without modifying any other components at all.

In the send place, we’re sending messages directly in the app component here,

        ModuleaMsg moduleaMsg = new ModuleaMsg();
        moduleaMsg.msg = "send from app";
        EventBus.getDefault().post(moduleaMsg);

        ModulebMsg modulebMsg = new ModulebMsg();
        modulebMsg.num = 10086;
        EventBus.getDefault().post(modulebMsg);

Copy the code

Modulea and ModuleB received the following message:

Module A received msg = send from app
Module B received msg = 10086

Copy the code

Above is all the way to use, if there are still interested partners then go down to see the implementation principle.

2. Implementation method

AfterEvaluate is a plugin for gradle. After evaluating is a plugin for gradle. After evaluating is a plugin for Gradle.

project.afterEvaluate {

            def eventModule = project.rootProject.childProjects.get(project.EventPluginExt.eventModuleName)
            if (eventModule == project) {
                throw new GradleException('event module should not be itself! ')}if (eventModule == null| |! eventModule.projectDir.exists()) {throw new GradleException('event module is not exists')
            }

            def cleanEventTask = project.task("eventClean", type: Delete) {
                delete("${project.rootProject.projectDir}/${eventModule.name}/src/main/java")
            }

            project.tasks.getByName("clean").dependsOn cleanEventTask
            ...
}

Copy the code

First determines whether there is a project in engineering. EventPluginExt. EventModuleName (= the event) this module, you can see in the first picture there is a event this module, which contains all the message type

Then define a file that clears the old.event suffix under the component, taking precedence over Gradle Cleantask.

And then you go down, and then you get all the components, divided into two types,lib modules or application modules

getVariants().all { variant ->
                def variantName = Utils.captureName(variant.name)
                System.out.println("variantName is " + variantName)
                if (isLibrary()) {
                } else{}}private boolean isLibrary(a) {
        return project.android.hasProperty('libraryVariants')}Copy the code

Take a look at the code below the lib module, divided into three parts, first part:

def intoAsset
def assetDir = Utils.getOneAssetDir(variant)
if (assetDir == null) {
     intoAsset = "${project.projectDir}/src/main/assets/$eventFolder"
} else {
     intoAsset = assetDir.absolutePath + "/$eventFolder"
}

def intoEventSdk = "${project.rootProject.projectDir}/${eventModule.name}/src/main/java"

List list = new ArrayList()
variant.sourceSets.each { sourceSet ->
sourceSet.java.srcDirs.each { file ->
list.add(file)
}
}

def javaSrcList = list as String[]

Copy the code

The assets folder in lib is stored in the eventFolder folder in assets folder in APK. Then define the file location of the Event component in the root directory, and finally get all the code files in the lib directory.

Then look at part 2, which defines tasks:

  1. eventLibCopyTaskIt’s a copy task. Get what you got earlierjavaSrcListIn the.eventSuffix of the file copied toeventModule, and modify the suffix name.java, before preBuild is executed
  2. Get the first one in the build processtaskpreBuild
  3. Defining a Copy TaskeventAssetsCopyTaskSRC /javasrc//.event->assets/cocoFolder//.event
  4. Get the generateAssets task
  5. Get the mergeAssets task
  6. Gets the current variant compilation tasks – such as assembleDebug/assembleRease
  7. Define the delete task – Delete assets/cocoFolder/ in assets under lib/The event file

Task eventLibCopyTask = project.task("eventLibCopyTaskFor" + variantName, type: Copy) {
                        includeEmptyDirs = false
                        from javaSrcList
                        into intoEventSdk
                        include "**/*.${project.EventPluginExt.postFix}"
                        rename "(.*)${project.EventPluginExt.postFix}".'$1java'
}

Task preBuildTask = project.tasks.getByName("pre" + variantName + "Build")

Task eventAssetsCopyTask = project.task("eventAssetsCopyTaskFor" + variantName, type: Copy) {
                        includeEmptyDirs = false
                        from javaSrcList
                        into intoAsset
                        include "**/*.${project.EventPluginExt.postFix}"
}

Task generateAssetsTask = project.tasks.getByName("generate" + variantName + "Assets")

Task mergeAssetsTask = variant.mergeAssets

Task assembleTask = variant.assemble

Task eventAssetsDeleteTask = project.task("eventAssetsDeleteFor" + variantName, type: Delete) {
                        delete intoAsset
}

Copy the code

The third part is to define the execution sequence of the above tasks. The first step is definitely to copy the. Event file to the event module.

2) preBuild 3) Copy lib events to Assets 4) generateAssets 5) mergeAssets 6) Delete events from Assets 7) assembleCopy the code
preBuildTask.dependsOn eventLibCopyTask

generateAssetsTask.dependsOn eventAssetsCopyTask
mergeAssetsTask.finalizedBy eventAssetsDeleteTask
assembleTask.finalizedBy eventAssetsDeleteTask

// Add the precompiled dependency of event to avoid the problem that the class cannot be found when the event module executes the event copy task of the current project after the compilation is completed
def preEventBuildTask = null
try{
       preEventBuildTask = eventModule.tasks.getByName("preBuild")}catch (Exception ignored) {

}

if(preEventBuildTask ! =null) {
       preEventBuildTask.mustRunAfter eventLibCopyTask
}

Copy the code

The application module will have a dependent AAR, which may contain. Event files, so you need to loop these files from the dependency to the Event module

The first step is to move the.event file from the AAR dependency to the Event module in the project directory

Task appUnpackTask = null
if (variant.hasProperty("compileConfiguration")) {
       // For Android Gradle plugin >= 2.5
      Attribute artifactType = Attribute.of("artifactType". String) FileCollection classPathConfig = variant.compileConfiguration.incoming.artifactView { attributes { it.attribute(artifactType,"aar")
             }
}.files
System.out.println("classPathConfig = " + classPathConfig)

appUnpackTask = project.task("eventAppUnpack" + variantName, type: Copy) {
          includeEmptyDirs = false
          classPathConfig.each {
                from(project.zipTree(it))
          }
          into "${project.rootProject.projectDir}/${eventModule.name}/src/main/java"
          include "**/$eventFolder/**/*.${project.EventPluginExt.postFix}"
          rename "(.*)${project.EventPluginExt.postFix}".'$1java'
          eachFile {
                it.path = it.path.replaceFirst(".*$eventFolder".' ')}}}Copy the code

The second step is to copy the app module suffix file to the event module, the logic is the same as the previous lib module, the implementation effect is javasrc/*. Event ->event/ SRC /javasrc/*/*. Java will not be introduced again

List list = new ArrayList()
variant.sourceSets.each { sourceSet ->
sourceSet.java.srcDirs.each { file ->
           list.add(file)
 }
}
def javaSrcList = list as String[]
System.out.println("javaSrcList is " + javaSrcList)

def intoEventSdk = "${project.rootProject.projectDir}/${eventModule.name}/src/main/java"

Task eventAppCopyTask = project.task("eventAppCopyTaskFor" + variantName, type: Copy) {
                        includeEmptyDirs = false
                        from javaSrcList
                        into intoEventSdk
                        include "**/*.${project.EventPluginExt.postFix}"
                        rename "(.*)${project.EventPluginExt.postFix}".'$1java'
}
if(appUnpackTask ! =null) {
     Task preBuildTask = project.tasks.getByName("pre" + variantName + "Build")
     preBuildTask.dependsOn(appUnpackTask)
     preBuildTask.dependsOn(eventAppCopyTask)

     eventModule.tasks.getByName("preBuild").mustRunAfter appUnpackTask
    eventModule.tasks.getByName("preBuild").mustRunAfter eventAppCopyTask
}

Copy the code

3. Summary

That’s all. The main purpose of this plug-in is to enable automatic compilation of message types, which can aid in better decoupling of components, and component developers do not need to contract or modify the base library to achieve data transfer purposes. One thing to note is that currently only aar modules are supported, jar packages are not supported. This article is also part of the componentization content, and will be updated later with some componentization knowledge used in large factories.

Because the level is limited, have wrong place unavoidable, rather misdirect others, welcome big guy to point out! Code word is not easy, thank you for your attention!

🙏 If you are studying with me, you can follow my official account — ❤️ Program ape Development Center ❤️. Every week, we will share the technology of Android regularly.