The background,

What happens when our projects get bigger, our code gets fatter, there’s more coupling, compilations are slower, and development is less efficient? At this point, we need to refactor the old project, that is, to break up the module, officially known as componentization.

Second, the introduction of

So what is componentization? Its basic idea is: common functions, controls, basic classes, third-party libraries, permissions and other common parts removed from the package, we call it basic components (baselibs); Business is divided into N modules for independent management, and each module is called business component. All business components need to rely on the encapsulated base components, business components do not rely on each other, so that each business module can run independently. The module of the whole project is encapsulated in the APP layer.

The jump between service modules can be realized by Arouter. Communication between business modules can be achieved through messages (EventBus).

Iii. Infrastructure construction

1. Component frame diagram

2. Project structure drawing based on component frame drawing

3. Each module is described next

There are altogether five modules in the project, including three business modules, one basic module and one APP shell module.

After building the project, we need to configure the switch of “integrated development mode” and “Component development mode” for the three modules. We can define the variable isModel in the gradle.properties file. IsModel =false means “integrated development mode”, isModel=true means “component development mode” (note: every change in isModel must be Sysn).

1) APP shell module

Gradle is configured to rely on different business components based on the isModel field in the configuration file.

2) Baselibs module

Mainly responsible for the encapsulation of common parts, such as MVP architecture, BaseView encapsulation, network request library, image loading library, tool classes and custom controls, etc.

To prevent duplicate dependencies, all third party libraries are placed in this module, and the business module does not make any third party dependencies, only relying on the Baselibs module.

The structure of the Baselibs module is as follows:

Gradle library introduced in the Baselibs module

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    configurations {
        all*.exclude group: 'com.android.support', module: 'support-v13'
    }
    testImplementation rootProject.ext.testDeps["junit"]
    androidTestImplementation rootProject.ext.testDeps["runner"]
    androidTestImplementation rootProject.ext.testDeps["espresso-core"]
    //leakCanary
    debugApi rootProject.ext.testDeps["leakcanary-debug"]
    releaseApi rootProject.ext.testDeps["leakcanary-release"] / / Support library API rootProject. Ext supportLibs request library API workLibs / / rootProject.ext.net / / network RxJava2 API rootProject.ext.rxJavaLibs // commonLibs api rootProject.ext.commonLibs kapt rootProject.ext.otherDeps["arouter-compiler"]}Copy the code

3) Service modules (module_NEWS, MODULE_VIDEO, module_ME)

Each business module exists as a library in integrated development mode; Exists as an Application in Component development mode and can be run separately.

Since the configuration files of each business module are similar, the module_news module is used as an example.

Here is the gradle configuration file for the module_news module:

if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
android {
    if (isModule.toBoolean()) {
        applicationId "com.cxz.module.me"
    }
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    defaultConfig {
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"
    }
}
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation rootProject.ext.testDeps["junit"]
    androidTestImplementation rootProject.ext.testDeps["runner"]
    androidTestImplementation rootProject.ext.testDeps["espresso-core"]
    implementation project(':baselibs')
    kapt rootProject.ext.otherDeps["arouter-compiler"]}Copy the code

4) Configuration filesconfig.gradle, the third library in the project,appVersion and other configurations

ext {
    android = [
            compileSdkVersion: 28,
            buildToolsVersion: "28.0.3",
            minSdkVersion    : 16,
            targetSdkVersion : 27,
            versionCode      : 1,
            versionName      : "1.0.0"
    ]
    dependVersion = [
            androidSupportSdkVersion: "28.0.0",
            espressoSdkVersion      : "3.0.2",
            retrofitSdkVersion      : "2.4.0",
            glideSdkVersion         : "4.8.0",
            rxJava                  : 2.2.2 "",
            rxAndroid               : "2.1.0.",
            rxKotlin                : "2.3.0",
            anko                    : "0.10.7"
    ]
    supportDeps = [
            "supportv4"        : "com.android.support:support-v4:${dependVersion.androidSupportSdkVersion}"."appcompatv7"      : "com.android.support:appcompat-v7:${dependVersion.androidSupportSdkVersion}"."cardview"         : "com.android.support:cardview-v7:${dependVersion.androidSupportSdkVersion}"."design"           : "com.android.support:design:${dependVersion.androidSupportSdkVersion}"."constraint-layout": "Com. Android. Support. The constraint, the constraint - layout: 1.1.3." "."annotations"      : "com.android.support:support-annotations:${dependVersion.androidSupportSdkVersion}"
    ]
    retrofit = [
            "retrofit"                : "com.squareup.retrofit2:retrofit:${dependVersion.retrofitSdkVersion}"."retrofitConverterGson"   : "com.squareup.retrofit2:converter-gson:${dependVersion.retrofitSdkVersion}"."retrofitAdapterRxjava2"  : "com.squareup.retrofit2:adapter-rxjava2:${dependVersion.retrofitSdkVersion}"."okhttp3LoggerInterceptor": 'com. Squareup. Okhttp3: logging - interceptor: 3.11.0'."retrofitConverterMoshi"  : 'com. Squareup. Retrofit2: converter - moshi: 2.4.0'."retrofitKotlinMoshi"     : "Com. Squareup. Moshi moshi - kotlin: 1.7.0"
    ]
    rxJava = [
            "rxJava"   : "io.reactivex.rxjava2:rxjava:${dependVersion.rxJava}"."rxAndroid": "io.reactivex.rxjava2:rxandroid:${dependVersion.rxAndroid}"."rxKotlin" : "io.reactivex.rxjava2:rxkotlin:${dependVersion.rxKotlin}"."anko"     : "org.jetbrains.anko:anko:${dependVersion.anko}"
    ]
    testDeps = [
            "junit"                    : 'junit: junit: 4.12'."runner"                   : 'com. Android. Support. Test: runner: 1.0.2'."espresso-core"            : "com.android.support.test.espresso:espresso-core:${dependVersion.espressoSdkVersion}"."espresso-contrib"         : "com.android.support.test.espresso:espresso-contrib:${dependVersion.espressoSdkVersion}"."espresso-intents"         : "com.android.support.test.espresso:espresso-intents:${dependVersion.espressoSdkVersion}"."leakcanary-debug"         : 'com. Squareup. Leakcanary: leakcanary - android: 1.6.1'."leakcanary-release"       : 'com. Squareup. Leakcanary: leakcanary - android - no - op: 1.6.1'."leakcanary-debug-fragment": 'com. Squareup. Leakcanary: leakcanary - support - fragments: 1.6.1'."debug-db"                 : 'com. Amitshekhar. Android debug - db: 1.0.4'
    ]
    commonDeps = [
            "multidex": 'com. Android. Support: multidex: 1.0.3'."logger"  : 'com. Orhanobut: logger: 2.2.0'."glide"   : 'com. Making. Bumptech. Glide: glide: 4.8.0'."eventbus": 'org. Greenrobot: eventbus: 3.1.1'."spinkit" : 'com. Making. Ybq: Android - SpinKit: 1.2.0'."arouter" : 'com. Alibaba: arouter - API: 1.4.0'
    ]
    otherDeps = [
            "arouter-compiler": 'com. Alibaba: arouter - compiler: 1.2.1'
    ]
    supportLibs = supportDeps.values()
    networkLibs = retrofit.values()
    rxJavaLibs = rxJava.values()
    commonLibs = commonDeps.values()
}

Copy the code

And finally, don’t forget that in engineeringbuild.gradleImport the configuration file

apply from: "config.gradle"
Copy the code

4. Interaction between business modules

The jump between service modules can be realized by Arouter. Communication between business modules can be achieved through messages (EventBus).

1. Arouter realizes the jump between service modules

We have previously relied on Arouter (see github.com/alibaba/ARo…). , it only takes the following two steps to realize the jump:

The first step

  • gradleconfiguration
kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
    generateStubs = true
}
dependencies {
...
    kapt rootProject.ext.otherDeps["arouter-compiler"]}Copy the code

The second step

  • You need to specify the target page and the parameters to take, and then callnavigation()Methods;

The third step

  • First of all inonCreateThe method callARouter.getInstance().inject(this)Injection;
  • And then want to use@RouteAnnotation annotation page, and inpathVariable to define a path to the page;
  • Finally, we define a field with the same name for the variable passed through@AutowiredVariable labeling,ArouterThe field is automatically assigned

2. EventBus implements communication between business modules

Manage messages using a third party such as EventBus. BaseActivity and BaseFragment classes in baselibs components encapsulate the message simply. Subclasses only need to override useEventBus() to return true to register the event.

5. Problems encountered in the construction process

1, the AndroidManifest

We know that when the APP is packaged, all the AndroidManifest is merged, so the Activity of each business component only needs to be registered in its own module.

If the business component is to run separately, it needs a separate AndroidManifest, which can be loaded in Gradle sourceSets.

Gradle configuration

android {
...
    sourceSets {
        main {
            if (isModule.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'// Exclude all Java files in the debug folder'debug/**'
                }
                kotlin {
                    exclude 'debug/**'}}}}... }Copy the code

Note: AndroidManifest in integration mode does not require Application configuration. AndroidManifest in component mode requires Application configuration separately and must inherit BaseApp.

2. Resource file conflicts

Resource files in different business components may have the same name. Therefore, resource file conflicts may occur. You can set resource prefixes to prevent resource file conflicts.

Gradle configuration, using the module_news module as an example

android {
...
    resourcePrefix "news_". }Copy the code

After this configuration, if we do not prefix the named resource file, the compiler will tell us that we did not prefix it.

At this point,AndroidThe basic componentization framework has been built, please correct any mistakes.

Five, the last

Full project address:Github.com/iceCola7/An…