Today’s tech news flash

Liu Ming state prosecutors announced recently not guilty, the event will not be charged, liu himself external sincere apology in a statement, said the behavior of the day the incident to the family, especially his wife caused great harm, has confessed to her the first time the facts, I hope she can accept the most sincere apology, must will try my best to make up for the matter of trauma to the family, Taking on the responsibilities of a husband again. At the same time, every colleague of JINGdong has been working under the pressure of public opinion, and his heart is full of guilt for them. In the future, he will devote more efforts to his work and make the enterprise develop better together with his employees.

Author’s brief introduction

A new week has begun. Nice to meet you all again! Tonight is Christmas Eve, I hope you don’t have to work overtime today, safe throughout the year.

This post from Goldze shares a set of componentized development solutions, which I believe will help you!

Goldze lot:

https://github.com/goldze

preface

About the componentization of Android, I believe that we are not unfamiliar, online about componentization of the article, as many as crucian crucian, but a componentization scheme based on MVVM mode is very few. Combined with its own research and exploration, in this share a framework based on MVVMHabit an Android (https://github.com/goldze/MVVMHabit) – Databinding modular development plan. The article is relatively simple and basic, there is no large length of one-way technology discussion points, the purpose is to let the developers who have learned this scheme can quickly get started to build MVVM component projects.

The overall architecture

Shallow light

The advantage of MVVM

MVVM is very popular in the front end of the Web. For example, VUE, Angular and React are all front-end frameworks implemented using the MVVM design pattern. While MVVM is not the only architectural pattern in Android development, the most commonly used is probably the MVC pattern (which is usually not the ideal implementation). The most popular is the MVP, which in some ways is very similar to the MVVM pattern. However, MVVM further improves the development efficiency on the basis of MVP, with the ability of data binding. When it comes to Android MVVM, it’s natural to think of Google’s Databinding, which provides XML and Java with the same seamless binding as HTML and JS. While MVVM on Android is not very popular yet, I believe it is a trend. Taking advantage of the popularity of MVVM on the Web front, the mobile front should also rise.

Componentized development

Code is dead, product is alive. In the daily development, a variety of frequently changing requirements, to the development of no small trouble. In order to make code as “live” as possible, design patterns emerge. However, it is still difficult to meet the needs of BT product with design mode alone.

For simple and small projects, most of them adopt a single project and develop independently. Since the project is small, compilation speed and maintenance costs are also acceptable. For a good App product, the App architecture of multi-person cooperation and single project is bound to affect the development efficiency and increase the maintenance cost of the project. Each developer should be familiar with so much code, it will be hard for many people collaborative development, and Android projects when compiling code computer is very card, again because of the severe code coupling under single engineering every change a code to recompile the packaging test, lead to very time consuming, the most important thing is that this code to do unit testing is a hindrance, So there has to be a more flexible architecture to replace the old monolithic engineering architecture.

With a component-based architecture, high cohesion, low coupling, clean code boundaries, each component can be split out and run independently. All components are entrusted to the host App, loading separate components and compiling their own modules, which is conducive to collaborative development by multiple teams.

MVVM mode + componentization

It’s no use just talking about theory. Let’s do something practical. There are two important frameworks here.

  • MVVMHabit: A set of rapid development library based on Google’s latest AAC architecture and MVVM design pattern, integrating Okhttp+RxJava+Retrofit+Glide and other mainstream modules to meet daily development needs. Using this framework, you can quickly develop a high quality, easy to maintain Android application.

  • ARouter: A framework for componentizing Android apps that supports routing, communication, and decoupling between modules.

MVVMHabit + ARouter: MVVM pattern + componentized solution, the former is the design pattern, the latter is the solution architecture, the two are used together, complement each other. With the support of these two frameworks, we can get twice the result with half the effort and quickly develop componentized applications.

Project structures,

Create a project

Create the most basic shelf in the project first, and then link it up step by step

1. Create a host

Building a componentized project starts with creating a regular project through Android Studio, just like a single project.

File->New->New Project…

The project created defines it as a “host” (as most people call it) or as an empty shell project. It has no layout and no activity. Its responsibility is to integrate the components developed by division of labor into one and package them into a usable Apk.

In the host project, there are two main things, one is androidmanifest.xml: configure application, launch page, etc. Gradle: is responsible for configuring build build/package parameters, depending on submodules.

2. Create a component

A Module is a library: apply plugin for merging and packaging. Library is an application: apply plugin: ‘com.android. Application’.

File->New->New Module->Android Library…

It is usually named module-xxx(component name)

3. Create a Library

In addition to the business components, you need to create two base libraries, library-Base and Library-Res.

  • Library-base: Holds common methods, common constants, contract classes for component communication, and so on. The upper layer is dependent on all components, and the lower layer depends on common library, image selection library, routing library and other common libraries. Through it, components can avoid direct dependence on various common libraries, connecting the previous and the next, as the core library of the whole componentization.

  • Library-res: In order to relieve the pressure of the Base library, a public library is separated, which is relied on by the Base library. It mainly stores public data related to res, such as images, style, anim, color, etc.

4. Prepare third-party frameworks

You also need to prepare two third-party frameworks, namely MVVMHabit and ARouter mentioned earlier

MVVMHabit:

allprojects {    repositories {        ...        google()        jcenter()        maven { url 'https://jitpack.io' }    }}Copy the code
dependencies { ... implementation 'com.github.goldze:MVVMHabit:? '}Copy the code

ARouter:

defaultConfig {    ...    javaCompileOptions {        annotationProcessorOptions {            arguments = [AROUTER_MODULE_NAME: project.getName()]        }    }}Copy the code
dependencies { api 'com.alibaba:arouter-api:? ' annotationProcessor 'com.alibaba:arouter-compiler:? '}Copy the code

Separation of components

Componentization is actually a process of separation — combination. Separation is the separation of product prototypes, and combination is the combination of code modules. After getting the demand, do not rush to start work, first of all, the product prototype is separated into a sub-prototype, after division of labor development, the completed sub-business module is packaged and combined into a complete Apk.

The most common is this bottom TAB design.

Componentization allows you to roughly split the project into a home page module, a work module, a message module, a user module, and, of course, a user module into an authentication module. The finer you break it down, the easier it is to reuse.

When creating a component above, create the following components: Module -home, Module -work, Module – MSG, Module -user, Module -sign

Component configuration

Gradle is the foundation of componentization. To build a good componentization project, gradle knowledge must be solid (Android has left its mark).

1. Dependencies

Once the project is created, it needs to be connected and put together. The dependencies are shown in the following figure:

Hosts depend on business components

Implementation Project (':module-sign'); implementation Project (':module-sign') Implementation Project (':module-home') // implementation Project (':module-work') // Message module implementation Implementation project(':module-msg') // implementation Project (':module-user')}Copy the code

Business components depend on library-base

Project (':library-base');Copy the code

Library-base relies on public libraries

Dependencies {//support API rootproject.ext. support["design"] API rootproject.ext. support[" appCompt-v7 "] / / library - res API project (' : library - res') / / MVVMHabit framework apis rootProject. Ext dependencies. MVVMHabit / / ARouter framework apis RootProject. Ext. Dependencies [" arouter - API "] / / other public libraries, such as image selection, sharing, push}Copy the code

2. Open the dataBinding

Android MVVM mode is dependent on DataBinding, which needs to be enabled in every component, including the host App

Android {// enable DataBinding DataBinding {enabled true}}Copy the code

3. Mode switch

A global variable is required to control whether the currently running project is in isolated or merged state. Defined in gradle.properties:

isBuildModule=falseCopy the code

IsBuildModule is true to make each component run independently, false to integrate all components into the host App.

4. The debug switch

Dynamically switch library and Application in a component’s build.gradle

If (isBuildModule.toboolean ()) {// Run the apply plugin as a standalone App: 'com.android.application'} else {// Run the apply plugin as a component: 'com.android.library'}Copy the code

When isBuildModule is true, it is an application with its own package name

Android {defaultConfig {// For standalone modules, use the package name of the current component if (isBuildModule.toboolean ()) {applicationId package name}}}Copy the code

5. The manifest configuration

The component is configured in its own Androidmanifest.xml. There is no need to add attributes to the application tag or specify the activity’s intent-filter. When merging is packaged, Gradle merges the AndroidManifest for each component into the host App.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.goldze.main">    <application>        ...    </application></manifest>Copy the code

When a component runs independently, you need a separate Androidmanifest.xml for debugging. Can be found in the SRC/main folder to create a u.s/AndroidManifest. XML. Configure the Application tag attribute and specify the activity to start.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.goldze.main">    <application        ...        >        <activity             ...            >            <intent-filter>                ...            </intent-filter>        </activity>    </application></manifest>Copy the code

And configure it in build.gradle

android { sourceSets { main { ... If (isBuildModule toBoolean ()) {/ / run independently manifest. SrcFile 'SRC/main/u.s/AndroidManifest. XML'} else {/ / merge to the host The manifest. SrcFile 'SRC/main/AndroidManifest. XML' resources {/ / formal version, Exclude 'SRC /main/alone/*'}}}}} from all debug files in the alone folderCopy the code

6. Unify resources

Configure the uniform resource prefix in the component’s build.gradle

Android {// uniform resourcePrefix, standard resource reference resourcePrefix "component name _"}Copy the code

You can extract a module.build.gradle from the common part of each component

If (isBuildModule.toboolean ()) {// Run the apply plugin as a standalone App: 'com.android.application'} else {// Run the apply plugin as a component: 'com.android.library'}android { ... defaultConfig { ... / / ali routing framework configuration javaCompileOptions {annotationProcessorOptions {the arguments = [AROUTER_MODULE_NAME: Project.getname ()]}}} sourceSets {main {if (isBuildModule.toboolean ()) {// Run manifest.srcfile independently 'SRC/main/u.s/AndroidManifest. XML'} else {/ / merge to host the manifest. The srcFile 'SRC/main/AndroidManifest. XML' resources { // Exclude 'SRC /main/alone/*'}}}} buildTypes {... } dataBinding { enabled true }}Copy the code

Add module.build.gradle to the component

apply from: ".. /module.build.gradle"android {defaultConfig {// If it is a standalone module, If (isBuildModule.toboolean ()) {applicationId package name}} // Uniform resource prefix, Specification resource reference resourcePrefix "component name _"}dependencies {... }Copy the code

complete

The running effect is as follows:

So far, a most basic componentization project has been built.

Feasibility plan

Component initialization

The component has a separate manifest when it runs independently, that is, during debug, and of course you can specify the Application class to initialize. So when components are merged, there can only be one Application and it exists in the host App. How should components be initialized?

1. The reflection

Reflection is an approach to component initialization.

Define a ModuleLifecycleConfig singleton class under library-Base with two public methods: initModuleAhead(initialized first) and initModuleLow(initialized later).

Why define two initialization methods here?

The number of components will inevitably involve the order of initialization, the component depends on third-party libraries, some libraries need to be initialized as early as possible, some can be a little later. For example, ARouter’s init method, which is officially required as early as possible, can be written in the library-base initializer class onInitAhead and initialized first.

@Overridepublic boolean onInitAhead(Application application) { KLog.init(true); If (buildconfig.debug) {ARouter. OpenLog (); // Prints the log arouter.openDebug (); // Enable debug mode (if running in InstantRun mode, debug must be enabled! } ARouter. Init (application); // As early as possible, it is recommended to initialize return false in Application; }Copy the code

Define a component lifecycle management class called ModuleLifecycleReflexs, which registers the full path of the component’s initialization class name and dynamically invokes the initialization methods of each component through reflection.

Note: The Module class initialized in the component should not be confused

2. Initialize the interface

Define an IModuleInit interface, dynamically configure the Application, implement the interface by the component to be initialized, and initialize the interface in the Application of the host APP

Public interface IModuleInit {// Initialize priority Boolean onInitAhead(Application Application); // Initialize last Boolean onInitLow(Application Application); }Copy the code

3. Initialize the implementation

With both reflection classes and interfaces in place, create an initializer class in each component that implements the IModuleInit interface. Finally, the initialization method is called in the host’s Application

@Overridepublic void onCreate() { super.onCreate(); / / initialize components (top) ModuleLifecycleConfig. GetInstance (). InitModuleAhead (this); / /... / / initialize components (on the) ModuleLifecycleConfig. GetInstance (). InitModuleLow (this); }Copy the code

Finally, the initialization effect of the component is realized

Minor optimizations: When the component is running independently, the host App does not execute the onCreate method, but the component business needs to initiate separate debugging. It is common practice to define applications separately in components, but creating an Application for each component can be cumbersome. With the above initialization methods, we can define a DebugApplication in the Library-base. The code in the Debug package does not participate in the compilation, but only initializes the data as a separate module run time. Finally, remember to specify the DebugApplication in base under the debug version of the component alone/AndroidManifest.

Intercomponent communication

There is absolutely no coupling between components, but there will definitely be business cross in the actual development. How to achieve communication between components without connection?

1. ARouter

ARouter is the core of the entire componentization because of its powerful routing mechanism. ARouter depends on library-Base, and all components depend on Library-Base, so it can be seen as a communication bridge between components.

Jump from component A to component B:

ARouter.getInstance()    .build(router_url)    .withString(key, value)    .navigation();Copy the code

Receive the parameters passed from the component B page:

@Autowired(name = key)String value;Copy the code

More ARouter usage:

https://github.com/alibaba/ARouter/blob/master/README_CN.md

2. Event Bus (RxBus)

RxBus is provided in MVVMHabit as a communication tool for global events.

When the component B page needs to send back data to component A, it can call:

_Login _login = new _Login(); RxBus.getDefault().post(_login);Copy the code

Register receive in component A (registration is done before invocation) :

subscribe = RxBus.getDefault().toObservable(_Login.class) .subscribe(new Consumer<_Login>() { @Override public void Accept (_Login L) throws Exception {// Refresh data initData() after a successful login; // Revoke RxSubscriptions. Remove (subscribe); }}); RxSubscriptions.add(subscribe);Copy the code

The base specification

Library-base has two main functions: it relies on a common base JAR or third-party framework, and it holds some common static properties and methods. Here are some convention specifications for basic generic classes.

1. config

Under the Base config package, the global configuration files are stored uniformly, such as component lifecycle initialization classes: ModuleLifecycleConfig, ModuleLifecycleReflexs, network ROOT_URL, SD card file read and write directory, etc

2. contract

RxBus component communication, need to go through the Base layer, unified specification. You can then define the RxBus contract classes under the Contract package, annotating them for use by other component developers.

3. global

The Key is used to store global keys, for example, IntentKeyGlobal: the Key name used to store the parameters to be transferred between components. SPKeyGlobal: Where the global SharedPreferences Key is stored. Internal keys within a single component can be defined separately in a separate component.

4. router

ARouter Route @route annotation Path can be separated from one or two RouterPath classes, such as defining a RouterActivityPath:

Public Class RouterActivityPath {/** * public static class Main {private static final String Main = "/ Main "; Public static final String PAGER_MAIN = MAIN +"/ MAIN "; }Copy the code

The Activity routing path is defined uniformly in this class, and the static internal class is used to block out the routing path in each component.

conclusion

I have to sum it up

Componentization of projects, just like manufacturing, most industrial products in life. Cars, for example, are made up of engines, wheels, engines and other important parts. Similarly, our APP is composed of various components connected in parallel to form a complete executable software. Its essence is such three points: independence, integrity, free combination. And componentization isn’t even a human invention. Even in nature, this is a pattern that already exists. Think about how complex our bodies are, no less than the Windows operating system. But apart from a few very important organs, most of us can survive partial damage or loss. This is the miracle of componentization.

When writing software, you must pay attention to the architecture. Don’t wait until the code gets worse and worse and worse and worse and worse and worse and worse and worse and worse and worse and worse. Finally, you can’t even look at yourself any longer before you think of refactoring. Componentization is a good way to isolate each business module so that if one of the components fails, it doesn’t have to be debugged as a whole as a single project. With the MVVM design mode, the specific projects in our work become lighter, easier to assemble, faster to compile and build, which not only improves work efficiency, but also further improves my knowledge of mobile application development. Componentized framework is universal, especially suitable for large and medium-sized projects with many iterations of business modules. It is a good solution.

The evolution of the Android architecture, from modularity to componentization to plug-in. On the road of component-based development, we try our best to improve the component development specifications and enrich the component function library. Some business components with large granularity can be further refined to achieve a single cohesion of component functions. At the same time, based on the existing component-based framework, it is convenient to build plug-in framework in the future.