No matter what everyone in the world says, I think my feelings are right. No matter what other people think, I never break my rhythm. Like things naturally can insist, do not like how also can not long.

— Haruki Murakami

background

As the App continues to iterate, the business will become more and more complex, with more and more business modules and more and more code for each module. To deal with this scenario, we need to divide the different business modules into components and modify the business code only in the corresponding modules. The robustness and stability of the project are ensured by high cohesion and low coupling business modules. Now the question is, how do we manage business components as the number of components increases?

Original statement: This article is an original article, it is strictly prohibited to reprint without the consent of the blogger. The previous article is to share my blog, now in the column re – hair.

Why do we use Gradle to manage components?

Let’s start by looking at what Android componentization needs to accomplish. (What is componentized build?)

  1. Divide modules according to business logic
  2. The project module can start tests independently
  3. The ability to introduce or remove certain business modules as required
  4. Through the combination of different modules, different APPS are formed

As for the first point: the need to divide modules according to technical architecture and business architecture, this needs to be considered according to the actual situation. We need to optimize the second, third and fourth points.

For the second point: Android determines whether the module is built in App mode or library mode by applying com.android.application or com.android.library. The biggest difference between App mode and Library mode is that apps can start, while Library can’t. So if our module can start independently, we need to manually change the build.gradle file of the module each time. It is a good idea to define a Boolean value to determine if you are in debug mode, but the problem is that not every module can be started independently. So no matter what kind of scheme is adopted, we need to manage it manually.

For the third point: after we develop business modules, we may need to add or remove some business modules frequently. If this is the case, we also need to manually modify the build.gradle of our App frequently.

As for the fourth point: sometimes we may refer to the same components in different apps (for example, didi’s regular and Enterprise versions, which contain the functions of the enterprise version), we also don’t want to frequently manually manage component dependencies, especially if the components can still run independently.

So, one of the biggest problems when we practice componentization is that we often need to manually build. Gradle files to manage plugin and App dependencies for component applications.

Use Gradle to manage components

Gradle plugin: Calces If you think this plug-in is useful, you can click star. If you have better ideas, you can submit pull Request to me.

Without further ado, here’s a quick process for implementing a componentized Build for Android through Calces.

Demo address: SimpleCalces

Project Structure:

  1. Introduce dependency libraries in Gradle 2.1 and later plug-in build script code: in the project build. Gradle

    buildscript {
        ...
    }
    plugins {
      id "calces.modules" version "1.0.11"
    }
    Copy the code

    Plugin build script code in older versions of Gradle or when dynamic configuration is required:

       buildscript {
         repositories {
           maven {
             url "https://plugins.gradle.org/m2/"
           }
         }
         dependencies {
           classpath "gradle.plugin.com.tangpj.tools:calces:1.0.11"
         }
       }
       apply plugin: "calces.appConfig"
    Copy the code
  2. Configure AppConfig in the project build.gradle

    appConfig {
        debugEnable false
    
        apps {
            app{
                modules ':library1'.':library2'
            }
        }
    
        modules{
            library1{
                mainActivity ".Library1Activity"
                applicationId "com.tangpj.library1"
                isRunAlone true
            }
            library2{
                mainActivity ".Library2Activity"
                applicationId "com.tangpj.library2"
                isRunAlone true}}}Copy the code
  3. Introduce modules in modules to automate building plug-ins (note: you don’t need to manually configure com.android.library or com.android.application)

    apply plugin: 'calces.modules'
    Copy the code

So we have componentized builds, yes, we don’t have to manually manage individual components and App builds anymore, with Calces, we can quickly componentize builds and build multiple apps at the same time.

So, how do we communicate between components? In simple projects, it is recommended to use Android’s implicit Intent to communicate between components, as in this Demo. For medium and large projects, I recommend using Ali’s routing solution: ARouter. The specific use method can refer to the official document, the use method is very simple.

Note: * * * * in the use of implicit Intent implementation component need to pay attention to when a communication can’t find the corresponding component abnormality: Java. Lang. An IllegalStateException: Could not execute method of the activity. This exception is caused by the failure to find the target component, so in the actual development, you need to catch this exception and handle it according to the actual situation of the project. Using the ARouter framework, exception handling can be implemented by setting a de-escalation policy (see the ARouter documentation for more information).

If you just want to implement simple componentization of the project, this is enough. If you want to implement a more flexible componentization architecture, you can read on. Here is a comprehensive analysis of the advantages of componentization and the ideas of componentization that I have summarized.

A brief introduction to componentized construction

Componentized building is more of an idea than a technology. Componentalized construction is an architectural idea that the project is overbloated and the code is too complex by redividing the project into modules with high cohesion and low coupling.

We took an in-depth look at Android componentization by splitting Google’s official architecture Demo, Demo Todo-MVP.

Demo address: TodoCalces

The Todo series of apps are a series of demos written as part of the Google Android-Architecture project to demonstrate the best implementation of the Android architecture. The todo app is simple enough, with little code and easy to understand. But it’s not too simple, because it’s an App with completion. It realizes the functions of task list, task details, new task, edit task, statistics task.

Todo-mvp implements the following features:

  • The task list
  • Task details
  • Add/edit tasks
  • Statistical work

We will divide the function of Todo-MVP into four business modules, and divide the bottom layer into two modules, namely superLib (which provides MVP architecture support and some other support library functions) and dataLib (data support module, Room provides the bottom database support function). For large projects, you can also add the resLib support module, which is used to store public image resources, character-wearing resources, styles, etc.

The structure division diagram is as follows:

As can be seen from the architecture diagram, all business components depend on the underlying library, while APP relies on the business component, which exists as an independent component here. In general componentization practice, APP is not included, and the existence of APP component is meaningful.

First of all, apart from the independent management of components and the dynamic configuration of the components on which the APP depends, our componentization also has a very important purpose: to package multiple different apps by combining different components. For example, QQ has a regular version and a chat version. The chat version is a simplified version of QQ. If we use componentization to manage projects, we simply remove unnecessary modules. The APP component’s role here is to act as a box to bring in the required components. And we can configure different APP styles by controlling the style of the packaging box. This can be done by using Style in the Application.

Let’s take todo-MVP as an example. For example, we need to implement a Todo APP that does not contain statistics. According to our principle, we only need to remove the statistics dependency.

The structure division diagram is as follows:

If noStatSitCs requires a different color scheme, just configure the theme in the Application TAB of the AndroidManifest.

Componentize todo-MVP using Calces

With the above analysis, let’s try to divide the todo-MVP project into components by business function. Let’s take a look at the partition:

Ok, we have a preliminary breakdown of the Todo-MVP project. According to the theory analyzed above, our business module can run independently, and we can quickly build an APP without statistics module.

We just need to use Calces to quickly implement the functionality we need.

Following the Calces tutorial, we learned that there are only three steps to implement Calces:

  1. Introducing dependency libraries
  2. Configure AppConfig in your project’s build.gradle
  3. Introduce module automation architecture c persistence into business modules

The first and third points are the same in all other projects and are not covered here. Let’s look at how we configure AppConfig for the TodoCalces project.

appConfig {

    debugEnable false

    apps {
        app {
            mainActivity "com.tangpj.tasks.TasksActivity"
            modules ':modules:addtask'.':modules:taskdetail'.':modules:tasks'.':modules:statistics'
        }

        app2 {
            name 'nostatistic'
            applicationId 'com.tangpj.nostatistic'
            modules ':modules:addtask'.':modules:taskdetail'.':modules:tasks'
        }

    }

    modules {
        addtask {
            name ":modules:addtask"
            applicationId "com.tangpj.addtask"
            mainActivity ".AddEditTaskActivity"
            isRunAlone false
        }

        taskdetail {
            name ":modules:taskdetail"
            applicationId "com.tangpj.taskdetail"
            mainActivity ".TaskDetailActivity"
            isRunAlone true
        }


        task {
            name ":modules:tasks"
            applicationId "com.tangpj.tasks"
            mainActivity ".TasksActivity"
            isRunAlone true
        }

        statistics {
            name ":modules:statistics"
            applicationId "com.tangpj.statistics"
            mainActivity ".StatisticsActivity"
            isRunAlone true}}}Copy the code

According to AppConfig, we have configured two apps, app1 and app2 respectively. And app2 does not rely on statistics. Now a comparison of our two apps running.

App1 (with Statistics module) :

App2 (without statistics module) :

As can be seen from the operation diagram, app1 and App2 have different color schemes, and app2 does not have statistics module, so it can quickly realize componentized construction by implementing reasonable division of projects and introducing Calces.

** Conclusion: ** It is easy to manage business components and build multiple apps through Calces. When we had only four business components, Calces might not have the advantage, but when we had 40, Calces would have the advantage. We can build multiple apps quickly by flexibly relying on different components. Like the Calces introduction pattern, use components as building blocks.

How to test

Android automated test development is a very large and not simple project, here THE author is not going to expand on. This is just a brief introduction to how componentized builds make testing easier.

Not all automated tests are created equal, and they often vary in scope, implementation difficulty, and execution time. We generally divide automated testing into three categories:

  1. Unit tests: The goal is to test the smallest unit of code. In a Java-based project, this unit is a method. Unit tests are easy to write, fast to execute, and provide valuable feedback on the correctness of your code during development.
  2. Integration testing: Tests a completed component or subsystem to ensure that interactions between multiple classes are working as expected. Integration tests take longer to execute than unit tests, are harder to maintain, and the cause of failure is harder to diagnose.
  3. Functional testing: Typically used to test the end-to-end functionality of an application, including interaction with all external systems from the user’s perspective. When we talk about the user perspective, we usually mean the user interface. Because the user interface changes over time, maintaining functional test code can become tedious and time-consuming.

To optimize return on investment, the code base should contain a lot of unit tests, a few integration tests, and fewer functional tests.

As shown below:

As we know from above, when our component is partitioned, a base dependency library (superLib) is partitioned. The base dependency library provides basic support for our project and is stable in the project and does not contain business logic, so we should use unit tests extensively in the base dependency library. Integration tests are used in our data dependency library (dataLib) to verify the interaction between production code and data modules. And our business module contains a large amount of business logic, this part is often part of the change, we can write some for our business module UI test automation code, but because of the business (interface) change often, so this part of the test code is difficult to maintain, and reuse is very low..

In the end, we came to the conclusion that you should focus on unit testing, so if you don’t have enough energy to write all the test code, you should focus on unit testing instead of functional testing for the least benefit.

So much for my advice on automated testing. If you need to learn more about testing, look it up, or follow my blog. A future blog post is likely to be about automated testing.

summary

With the Calces plug-in, we only need to focus on how to properly partition the architecture of components and how to achieve communication between components when implementing Android componentization. There are two major issues with Android componentization:

  1. How to divide component modules properly for large projects
  2. How to manage a project with a large number of components

The second problem can be solved quickly through Calces. As for the first problem, the author gives guidance that business modules should be as small as possible, because the smaller the module, the easier it is to achieve the goal of high cohesion and low coupling. You don’t need to worry that your project modules are too thin to manage, because Calces can easily manage them for you.

History of hits

Data Binding Compiler V2 – The foundation for building the entire Android MVVM

About me

If this article inspires you, follow me on my instagram account or GitHub.

Scan and focus on me: