Understanding of Android componentization/modularization!

Original source

Title: Understanding of Android componentization/modularization!

Author:The moving turtle

Original link: Understanding Android componentization/modularization!

Foreword

Nowadays, componentization is really not a new thing. Large companies are very familiar with it and are very familiar with it. That is to say, some medium-sized projects and small projects are now working hard on the road of componentization. So comrades, those who have never played with componentization and those who are not familiar with it should start it quickly. To put it bluntly, if you don’t know how to componentize, releasing a shadow project will be a nightmare for you. In essence, any technological progress is made by scratching one’s head and scratching one’s head and exhausting countless hairs under the pressure of practical needs. Haha, it’s just a joke here. So the componentization thing has been around for so long, the page has been developed for so long, and more and more people are using it. It will definitely be of great benefit to our display development. If your friends don’t know it, hurry up if you are not familiar with it. How would you answer if I didn’t ask you in an interview?

Let’s talk formally about componentization

Componentization is actually not complicated. It is just an idea. It is essentially an app architecture idea. To put it bluntly, it is very simple. However, the difficulty lies in the componentization transformation. When actually writing the code, there will be many thorny problems. Of course Most of these pitfalls have been gone through by predecessors. I mainly record them here. If you see familiar parts, please don’t scold me. After all, they are all the stuff of my predecessors.

Let me add here that componentization is a kind of app architecture. Its development follows the normal technological development process and is also designed to encapsulate code for the purpose of high reuse and maintainability. The difference is that componentization is a Re-encapsulation of the entire app.

So much nonsense, so what is componentization? Do you want to worry about it? Before we talk about componentization in detail, we need to understand two concepts, namely the components and modules mentioned above.

First of all, components and modules are not officially stipulated. They are concepts that everyone has agreed upon after the development of these technologies. In fact, they are very simple and can be understood as soon as they are explained.

  • Module: The module in android is the business module, which refers to the business alone. It splits the app according to the business. For example, we make the order into one module, and the personal center into one module. Videos and audios are all made into modules, which are reflected in the app as modules. The Chinese meaning of module is also module. This is not allowed, this is Google’s hint to us. The purpose of modularization is to build building blocks. Just take a few modules and you can launch an app for anyone. Not to mention that the demand for shadow apps is very strong now. If you go and look at the projects of large companies, they are not just a bunch of shadows. As part of the project, Toutiao has also created a vest for Toutiao Video. This is actually taking out the video module and adding a startup page. Such examples abound. Otherwise, if you don’t know how to componentize shadow projects, it will be a nightmare for you. Haha, then don’t even think about maintenance. How many copies of the code will you have to make?
  • Component: This is just as simple. To put it bluntly, it is what we usually do, encapsulating functions. This is a component. A function is a component, IO, database, network Wait, these functions are all components, so you understand. In this case, why should we come up with the concept of a component? Of course, everything has its meaning, because components have very high and clear requirements for the encapsulation of functional code: encapsulate it in one place and use it everywhere. We need to maximize maintainability, reusability, scalability, and performance, because only in this way can we truly encapsulate it in one place and use it everywhere. Of course, the scope of components now covers a wide range. Everything in the app is a component. Basically, we are divided into: basic functional components, general UI components, and basic business components.

Above I talked about my own understanding of modularization and componentization, which is the current understanding of modules and components in development. In the development of modularization and componentization, the concepts have also been adjusted and changed. You only need to look at what it looks like now. If you are interested in in-depth study, you can take a look at the development history of componentization and modularization.

I think Android modularization exploration and practice has the best explanation of modularity and componentization concepts.

Componentization and modularization are now the same thing. If a project is regarded as a combination of bags, then the modules are the bags with the largest volume, the components are the bags with the smallest volume, and the large bags are the most directly accessible bags. Bags that are observed and contacted externally. Big bags are also made up of smaller bags. It is an inappropriate metaphor. This is the relationship between modules and components. It is our understanding of the separation and encapsulation of business and functions.

Okay, now we have officially introduced componentization

In terms of engineering performance, componentization means that we divide the app into different modules according to its different businesses, and encapsulate various functions into libraries. Horizontal dependence between modules is strictly prohibited, otherwise they can be used alone. I can’t bring all related modules with me just to use one module. If such a module also has dependent modules, it would be nonsense to talk about reusability.

The main app is what we often call the shell project that depends on these modules. The library is dependent on the required modules, but the library version must be considered. With the expansion of business and functions, the number of libraries is also huge. When WeChat is split into components It is said that more than 80 modules have been split, which shows that the library is also indispensable.

Most of the time, module and library are provided by arr and jar to be referenced by the shell project. Arr and jar will not be compiled during compilation. Only the version will be checked and the latest version will be retained, which not only improves the compilation speed of the app, but also improves the compilation speed of the app. Also provides a resource conflict resolution method.

I’ll put some pictures below to describe componentization. Take a closer look, the pictures are much more vivid than the text.

How the project is componentized:

.

Component core: router

When we abstract modules, there is no interdependence between modules and they are strictly decoupled in order to achieve our purpose of reuse. If modules cannot depend on each other, they cannot call the code of other modules. So what should we do when faced with common needs such as calling up pages between businesses and communicating with each other? Yes, everyone is in the picture above. See things router.

Router is our unified inter-module communication protocol. In router we mainly deal with the following issues:

Page jump between modules
Data transfer between modules
Module initialization processing

There is a ready-made router, or you can encapsulate it yourself. The idea used is to use the router as a component. All business modules rely on this router component, and of course the shell app also. Then we register the required page jumps, data transfer, and initialization between modules into the router. Here are the Reflecting the importance of our definition of a unified and universal module communication protocol, the router maintains multiple collections to save this relationship, and then we can achieve communication between modules through the router.

The encapsulation of router is quite troublesome, and it is not easy to write it well. The ones that are used more now include:

  • Alibaba’s ARouter
  • The earliest ActivityRouter
  • Spiny’s router is my favorite. It is no longer maintained. The idea is great and the process issue is taken into consideration. Unfortunately, it does not use APT annotation technology.
  • Practice router

I introduced several routers above. Basically, whether you write it yourself or use a ready-made one, the routers basically look like the ones above. Of course, good routers now still need to use APT annotation technology to dynamically register the router. The module method is very useful if you write your own code to register it. Some problems are difficult to deal with. For example, if the static instance of the router is recycled and you create a new one, what about the module registration method? It is too troublesome to write, so why not APT annotations are convenient and scalable. Here is a ToyBricks_Android project modular solution that can solve the problem of APT not being able to scan arr packages.

Finally, communication between modules can actually be divided into three types:

  • Page call up
  • Notification of a certain event
  • Directly call the business methods of certain modules

The current router can complete this task very well when the page is called.

Notifications of certain events, such as when I switch cities, notify certain pages to display or refresh data. Depending on the business, this will have a wide impact and will affect multiple businesses. Because of the reusability of the module, We cannot determine which business modules will be specifically affected in the module, so it is more appropriate to use eventbus/broadcast in this scenario.

Directly calling the business method of the default module creates a strong business coupling between the business modules. There is nothing you can do about this when the product is designed like this. Generally, in such a scenario, it will ensure that the relevant business modules are loaded. , so it is more flexible to define the router, and it can call certain methods of the specified module.

I found another way of saying it, I like it very much and it is very close to my idea.
From: Android Architecture Design: MVC, MVP, MVVM and Componentization

The so-called componentization, the popular understanding is to divide a project into various modules, each module is decoupled from each other, and can be independently developed and compiled into an independent APP for debugging, and then the various modules can be combined to form a complete APP. . Its advantage is that when the project is relatively large, it facilitates division of labor, collaboration and synchronous development among developers; the divided modules can be shared between projects to achieve the purpose of reuse. Componentization has many benefits, especially for larger projects.

How to share and communicate data between various modules? We can divide the data that needs to be shared into a separate module to place public data. For data communication between various modules, we can use Alibaba’s ARouter to jump to the page, and use encapsulated RxJava as EventBus for global data communication.

router I don’t want to say too much, and I can’t say it well. For this part, you can read my last link, or you can also look at the four routes above. No matter how it is implemented, the router is to build an intermediate communication platform for the module module. That’s the purpose. Please read more carefully. I am also looking at others here.

Let me push it a bit further. For modules and libraries, we try not to provide source code dependencies. This does not meet the purpose of reuse. When you release several shadow functions or other apps, then you use the source code dependency method. How do you provide maintenance for modules and libraries, so try to use arr and jar. It would also be good if we can package some libraries with similar positioning into a module.

We must be familiar with the use of gradle. In componentization, we will use gradle extensively to provide various resource loading configurations and environment configurations.

The core purpose of componentization is high reusability, high maintainability and high scalability of code. The other advantages are all of a joint nature. We must first grasp the core points to learn, and other things are not important. We have time. Look again

Problems encountered in componentization

1. Submodules are compiled and tested separately

When doing component development, when we test the module library, we convert the module into an apk file separately. When publishing the module, we provide the library for external dependencies. This is achieved by configuring the module’s gradle compilation mode.

First, define constants in the submodule build.gradle to indicate whether the module is currently in development mode.

def isDebug = true

Configure the mode in the submodule’s build.gradle. Compile it into a standalone app in debug mode and compile it into a library in release mode.

1if (isDebug.toBoolean()) {
2 apply plugin: 'com.android.application'
3} else {
4 apply plugin: 'com.android.library'
5}

There are differences in the module AndroidManifest.xml file in the two modes. As an independently running app, it has its own Application and needs to add Launcher’s entry intent, which is not required as a library. This problem is easy to solve. Just write two different AndroidManifest.xml and configure them in gradle.

Configure in gradle script

ext {
 2 android_compileSdkVersion = 25
 3 android_buildToolsVersion = '25.0.2'
 4 android_minSdkVersion = 21
 5 android_targetSdkVersion = 25
 6
 7 lib_appcompat = 'com.android.support:appcompat-v7:25.1.1'
 8 lib_picasso = 'com.squareup.picasso:picasso:2.5.2'
 9 lib_gson = 'com.google.code.gson:gson:2.6.1'
10}

I haven’t tried the module of arr resource. Is it still possible to use this method?

3. Resource id conflict

The resource files of the module in Android will eventually be merged into the main project. The ID of the resource file will eventually be different from the ID of the module, so this will cause the problem of resource duplication. To solve this problem, our approach Just add a unified prefix to the module resource.

1andorid{
 2    ...
 3
 4 buildTypes{
 5...
 6}
 7
 8 resourcePrefix "moudle_prefix"
 9
10}

But note that the files in the res folder can be prefixed with the gradle script, but image resources cannot be used. We still need to add the prefix ourselves when naming image resources.

4. Application initialization problem

When the submodule serves as an application, some initialization work needs to be performed during Application.onCreate. When used as a library, this onCreate cannot be adjusted. So I wrote a static method for the Application of the main project to call.

public class ApplicationA extends Application {
 2
 3@Override public void onCreate() {
 4 super.onCreate();
 5 //Set context for the underlying library
 6 AppContext.init(getApplicationContext());
 7}
 8  /**
 9 * Contents that need to be initialized when used as a library
10 */
11 public static void onCreateAsLibrary() {
12 //Pass the instance of the interface to FunctionBus
13 FunctionBus.setFunction(new FunctionA() {
14 @Override public String getData(String key) {
15 return "xixi";
16}
17 });
18}
19}

Remember to initialize the submodule when Application onCreate of the main project.

public class MainApplication extends Application {
2
3 @Override public void onCreate() {
4 super.onCreate();
5 AppContext.init(getApplicationContext());
6 ApplicationA.onCreateAsLibrary();
7 ApplicationB.onCreateAsLibrary();
8  }
9}

In addition to providing methods to call in the shell project, it can also be done in combination with the router using APT technology. Using annotations, we don’t have to call it ourselves, which is completely decoupled.

5. Library dependency issues

Let me talk about a question first. In the componentized engineering model diagram, both the multimedia component and the Common component depend on the log component, and the A business component depends on both the multimedia component and the Common component. At this time, someone will ask, wouldn’t you do it this way? The log component will be repeatedly relied on, and the Common component will also be relied on by every business component. Isn’t this a problem?

In fact, there is no need to worry about this problem. If there is a problem of repeated dependencies, an error will be reported when you compile and package it. If you still don’t believe it, you can decompile the final packaged APP and look at the code inside and you will know. . Component is just a term we call for convenience during the code development stage. There is no such concept when components are packaged into APP. These components will eventually be packaged into arr packages, which are then relied on by the app shell project. When building APP During the process, Gradle will automatically exclude duplicate arr packages, and the same code will not exist in the APP;

But although the components will not be repeated, we still have to consider another situation. The third-party libraries we compile in build.gradle, such as the AndroidSupport library, are often dependent on some open source controls, and we will definitely compile ourselves. AndroidSupport library, this will cause the third-party package and our own package to be loaded repeatedly. The solution is to find out the extra library and exclude the extra library, and Gradle also supports this, respectively. Two methods: exclude based on component name or exclude based on package name. The following takes excluding the support-v4 library as an example:

dependencies {
2 compile fileTree(dir: 'libs', include: ['*.jar'])
3 compile("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion") {
4 exclude module: 'support-v4'//Exclude based on component name
5 exclude group: 'android.support.v4'//Exclude based on package name
6}
7}

The problem of repeated library dependencies has been solved, but when we develop projects, we will rely on many open source libraries, and each component of these libraries needs to be used. It would be very troublesome to rely on each component, especially for When upgrading these libraries, in order to facilitate our unified management of third-party libraries, we will provide a unified entrance for the entire project to rely on third-party libraries. One of the functions of the Common library introduced earlier is to uniformly rely on open source libraries, because other business components They all rely on the Common library, so these business components also indirectly rely on the open source libraries that Common relies on.

dependencies {
 2 compile fileTree(dir: 'libs', include: ['*.jar'])
 3 //Android Support
 4 compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
 5 compile "com.android.support:design:$rootProject.supportLibraryVersion"
 6 compile "com.android.support:percent:$rootProject.supportLibraryVersion"
 7 //Network request related
 8 compile "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
 9 compile "com.squareup.retrofit2:retrofit-mock:$rootProject.retrofitVersion"
10 compile "com.github.franmontiel:PersistentCookieJar:$rootProject.cookieVersion"
11 //stable
12 compile "com.github.bumptech.glide:glide:$rootProject.glideVersion"
13 compile "com.orhanobut:logger:$rootProject.loggerVersion"
14 compile "org.greenrobot:eventbus:$rootProject.eventbusVersion"
15 compile "com.google.code.gson:gson:$rootProject.gsonVersion"
16 compile "com.github.chrisbanes:PhotoView:$rootProject.photoViewVersion"
17
18 compile "com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion"
19 compile "com.github.GrenderG:Toasty:$rootProject.toastyVersion"
20
21 //router
22 compile "com.github.mzule.activityrouter:activityrouter:$rootProject.routerVersion"
23}

6. module uses different codes for different business environments

When we do projects, we will have at least two sets of environments: testing and online. Componentization makes us start to pay attention to gradle. Through gradle configuration, we can reduce a lot of code writing. We can also use gradle to switch environments. In an environment that fails, Register different code files, see the picture below

We have two environments, debug and release, which contain code executed in different environments. Main contains code parts that have nothing to do with environment switching. I just set up gradle like this.

1android {
 2    // ...
 3 sourceSets {
 4 debug {
 5 java.srcDirs = ['src/main/java', 'src/debug/java']
 6}
 7 release {
 8 java.srcDirs = ['src/main/java', 'src/release/java']
 9        }
10}
11}

In addition, when publishing the library, you need to specify some settings, as follows:

android {
2    // ...
3 defaultConfig {
4 // ...
5 defaultPublishConfig 'release'
6 publishNonDefault true
7}
8}

illustrate:

  • defaultPublishConfig ‘release’. The default library will only produce the version under release. This version will be used by all projects. DefaultPublishConfig can be used to control which version of the library is produced by default.
  • publishNonDefault true. By default, all versions of the library cannot be produced. By setting publishNonDefault to true, all versions of the library can be produced at the same time.
    Business component modules rely on libraries produced by different basic components, as follows:
dependencies {
2    // ...
3 debugCompile project(path: ':baselibrary', configuration: "debug")
4 releaseCompile project(path: ':baselibrary', configuration: "release")
5}
6

Using such a configuration script solves the problem of multiple APK packages relying on different libraries produced by the same component, and finally gets the development/test/production APK package we need.

Merge multiple modules into one folder

We use the project name + : colon to represent the module in studio when referencing it.

implementation project(':basecomponents')

Note that this only means that we want to reference the module with this name, and we don’t care about the address of this module here.

Then it can be understood that the address of the module can be configured at will. So where to configure the address of the module? The answer is in the setting.gradle file. We just specify its address for the module ‘:basecomponents’.

For example, we want to create a new folder named components to store our component module. We give 2 components (basecomponents,aaa), and then we specify the file path of each project in setting.gradle.

1include ':app', ':basecomponents', ':aaa'
2
3project(':basecomponents').projectDir = new File( 'components/basecomponents' )
4project(':aaa').projectDir = new File( 'components/aaa' )

A good component document is a must

Componentization is the future of Android development entering a new era. It is inevitable for code expansion and support rapid development. A good componentization document is also necessary nowadays.

Post a picture below

Component pit

Componentization is good, but there are also many pitfalls that are difficult to fill, especially databinding, dagger, and bufferkinft. This is caused by studio compilation problems.

Although the module in studio is code independent from the shell project, it still needs to be merged into the shell project during compilation. Otherwise, how to achieve an APK file? If there are multiple APK files, it will not become a plug-in. Plug-in There are more pits. A fundamental problem arises when merging module into a shell project is that the module’s R file value changes. The R file data of the module is not fixed. Only the R file of the shell project is a constant value, which is time-invariant. The R file value of the module will be determined after the resources of the module are merged into the shell project. Then this depends on The technology of compile-time annotation has caused problems. The R path you specified cannot be found in the end, and it is said that it also involves the final of the annotation. I don’t know, I saw someone saying this, so everyone is concerned about the annotation technology when developing components. You need to pay more attention to the framework, and if there are pitfalls, you need to look more carefully to climb over them.

Componentized articles:
Android Modular Exploration and Practice Anjuke’s time case explains the concepts very well and is suitable for beginners and students with confused concepts, especially the detailed demo is provided.

I have something to say about Android modularity, but I don’t know whether to say it or not. This is the most practical and good article I have seen so far. It is definitely worth reading.

Android componentization solution

https://blog.csdn.net/guiying712/article/details/55213884

This article explains it best and also provides a lot of thoughts on gradle configuration.

Android development: from modularization to componentization

https://blog.csdn.net/dd864140130/article/details/53645290)

Excellent componentization solution:

Android completely componentized solution practice

https://www.jianshu.com/p/1b1d77f58e84

Android completely componentized demo released

https://www.jianshu.com/p/59822a7b2fad