With the increasing number of developers, if reasonable development architecture is not used and certain code writing method is standardized, the code will become more and more bloated over time, and the maintenance cost will become higher and higher, and it will be difficult for the outgoing staff to hand over. Componentization is now a mature solution and the preferred solution for multi-team development. Our componentization reconstruction process is a process of standardizing various code structures and sorting out the existing business logic, removing the outdated codes and optimizing the unreasonable parts previously made.

Partition of components

  • Functional components

Named after lib_xxxx, like network request component (lib_http), picture picker component (lib_pictureselector)… The components that all of these modules may use and implement a single function are called functional components

  • The business component

It starts with module_XXXX and is split based on services, such as the login component (module_login), user personal information component (module_user), and home page component (module_main)

Frame drawing

The Demo sample figure

  • The app:

App shell project relies on all business components and does not store any logical code.

  • lib_common

The basic public module, the lowest level library, relies on a variety of functional components (lib_xxxx), third-party SDK (Uming, Bugly, etc.), secondary packaging of various tool classes and open source libraries, storage of various custom views (such as rounded imageView) and other things that have nothing to do with business.

  • lib_base

The service public module stores the common parts of each service component, such as BaseActivity, BaseApplication, basic domain name, and various global constants. All service modules only need to rely on this module, and do not need to rely on other modules.

  • lib_http

Copy github.com/zhou-you/Rx… Open source framework source code, with support interface data cache, support for multiple cache strategy, support for multiple domains, and other functions, can effectively help us to unify the whole APP requests, each module is only need to pay attention to own request what is interface, prevent simply use Retrofit writing a disorderly problem, don’t have to use this framework, Just to express the need for a highly customized networking framework within your APP, if your project is using MVVM architecture then you can build your own with Retrofit and Kotlin coroutines reference excellent frameworks.

  • lib_pictureselector

Third party open source picture picker

  • lib_xxxx…..

Module separate compilation

Compiling each function module separately into an independent APP is one of the most outstanding advantages of component development, which can help us reduce a lot of compilation time. We only need to pay attention to the functions being developed by our module. After all configuration, we only need to turn on the switch in config.gradle

There is a note of the points, compile time alone as user data, such as whether the login data won’t get it, because we are before in other modules access to these data, now equivalent to a a new APP without those things, we need to under the module requires a separate compilation of application directly write die some data, Such as user data (if useful), such as request interface needs some basic parameters (auth, token…) , then this part of the basic parameters should be written first.

Component initialization

For example, if a module needs to import a third party library from gradle, it needs to import only the third party library from gradle. This may cause problems. Some libraries need to be initialized in application. The base layer cannot be initialized because the library is not introduced. For this problem, please refer to juejin.cn/post/686662… In the BaseApplication layer, we need to initialize some common BaseApplication elements as follows:

Some of the third party SDKS are initialized late, partly for privacy reasons and partly to speed up startup. Here I use the form of creating a new IntentService to start the child thread:

Resource naming conflict

In order to avoid this situation, we need to configure the resourcePrefix field in gradle for each module, which specifies that each module’s resource file must start with its module name. AndroidStudio gets a warning if you don’t follow this rule.

Unified extraction of configuration files

To facilitate configuration information management, we extract all dependent third-party libraries, package names, targetSdkVersion, compileSdkVersion into config.gradle file

We know thatlib_commonAdd dependencies to the config file. Add dependencies to the config file. Add dependencies to the config file.

There are still many different types of config compileSdkVersion, buildToolsVersion, minSdkVersion, etc. For easy maintenance, extract these common ones and name them module-basic.gradle. Business module reference, as follows:

Communication between modules

  • Jump between modules

There is no connection between modules, so the jump is borrowed from Ali’s ARouter (github.com/alibaba/ARo… , can do a simple encapsulation in the base layer, ARouter specific use can refer to its Github, after encapsulation call as follows:

At the same time, ARouter supports obtaining Fragment instances. Like the usual APP home page structure is an Activity with four or five fragments, then you can write the Fragment under the corresponding module, and use the following call method in the main module to obtain the corresponding Fragment instance

  • Data communication between modules

So what I did was I just put some common things in the base layer, things like LoginUtil to determine the user’s login status, and that kind of put all the implementation logic in the base layer, so it doesn’t fit the componentization idea, the right idea is that each module implements each module’s logic, Implementation logic like LoginUtil should be done by the login module, and other modules just need to know how to call it. Specifically, a new module (e.g. Export_login (export_login) is used to store the exposed service (interface) required by login, and the specific implementation of interface is done in module_login. Any module that needs to be exposed depends on the exposed service component. See the article (juejin.cn/post/688111… The part where components communicate.

Subcontracting of each component shall be consistent

Each business component module should maintain a consistent subcontracting structure, so that sometimes we need to maintain the modules developed by others, it is convenient to get started, but also conducive to the project to maintain a consistent structure. The following subcontracting is just an example, it is not necessary to follow this subcontracting, just want to emphasize the need to maintain consistency.

Why split network requests into a separate library (lib_http)

Network request is a very important part of an APP. Before the revision, I encapsulated Retrofit with singletons, which brought the following problems:

  • ApiService is bloated, with hundreds of interfaces written together
  • If the App has multiple domain names, you need to create multiple RetrofitClients, which also looks unelegant
  • If a Post interface needs to be cached, it is poorly written. Either you need to manually determine whether the data is cached, or you need to combine Rxjava with the request code layer to do some merging operations, and there is no caching strategy
  • Some requests are external links, and you have to write a * that does not return BaseBean, but json
  • .

Based on this kind of situation, there will be a variety of writing methods, if more people, if the network request library function is not perfect, everyone to write a set, it will be very messy, I expect the result is:

  • Should the code for invoking network requests be the same for each business component no matter what
  • The API of a business component is managed by its own module; other modules do not need to know about it
  • Support various cache policies, support multiple domain names, multiple request methods and other functions
  • .

The bottom layer requires one such as RxEasyHttp(github.com/zhou-you/Rx… OkGO (github.com/jeasonlzy/o… If you use MVVM+Kotlin, you can use Kotlin+ coroutine to create your own network request library.

The following is an example of the previous use of componentized projects:

Each business module only needs to care about which interface it requests, what parameters it needs, what data it returns, and how to use it.

In any case, the outer layer should maintain a uniform invocation (support various configurations of separate interfaces, such as caching policies…). Keeping it all the same will help us in the future when we don’t want to use Retrofit and we want to use another library, we just need to change itlib_httpBusiness component logic code does not need to be modified.

Image secondary packaging based on Glide

Why encapsulate? Because of directly using the Glide of writing long, 2 it is can let each business module invocation style is unified, same as above, if encapsulation of good, don’t want to use Glide after one thousand, want to change other images load libraries, we don’t need to change to the business logic, I this writing is directly encapsulated in the base layer, If we want to split the lib_imageloader feature separately, we can write it:

Call as follows:

What if there are multiple business modules sharing an interface?

What I am doing now is to extract this interface to the base layer as a public Url. If more than four or five modules use it more than once, I will create an IntentService to request it, and then use Eventbus to expose the request result, which is probably not the best way.

Screen adaptation scheme

Screen adaptation using byte beating modify DisplayMetrics scheme: mp.weixin.qq.com/s/d9QCoBP6k… Many great god based on this scheme, the open source some libraries, like AutoSize, but there are still sometimes because adapter failure or somehow the screen switch lead to problems in terms of style all messing, especially some system play a window, somehow the screen needs to display different popup window, are more likely to reflect this problem, this problem is to use the dp adapter, Here you can use blankJ’s PT unit adaptation to reduce the problem of adaptation failure. For details, please refer to blankj.com/2018/12/18/… The disadvantage of this scheme is that it will cost a lot to change the layout, because dp and SP need to be changed into PT.

Common third-party open source libraries

A good third-party library can greatly improve our development efficiency, and at the same time, it is more conducive to maintaining A consistent code style in multiplayer development. Imagine A DeviceUtils method to obtain A unique device ID. Colleague A writes A method, while B does not know that A has written it, he writes another method. Colleague C creates a new PhoneUtils and writes a new one. The code will become more and more redundant and messy:

  • Utils library:Github.com/Blankj/Andr…

Good utils library, if we need a certain XXUtils library, we should be consistent and look for methods in one place first, if not add to the base layer and spread to other colleagues

  • Recycleview Adapter (BRVAH)Github.com/CymChad/Bas…

Recycleview Adapter If we use the native way, like multi-item layout, item click event what is also written a lot, use BRvah effectively to help us unify various writing methods, convenient for us to read other colleagues’ code.

  • .

These libraries are tens of thousands of star is very good library, but not necessarily use, if we think that the seal is better than others, you can also write a set, mainly we want to prevent some template code repetition, and then everyone repeat the appearance is not the same…..

MVVM+Kotlin

MVVM+Kotlin+Jetpack is now mainstream, we should use ViewBinding and other components like ButterKnife and Lifecycle to avoid memory leaks. Use Livedata, DataBinding and other components to data-driven UI, reasonably split the logic into ViewModel, Repository and UI layer according to the business +MVVM idea, and prevent thousands of lines of code in one Activity, which will greatly increase the maintenance cost. The official MVVM architecture diagram is as follows:

Refer to the article

For a more detailed introduction to componentization, refer to the following articles:

Juejin. Cn/post / 684490…

Juejin. Cn/post / 686662…

Juejin. Cn/post / 688111…