Quick start and use of componentization/modularization: Componentization is realized through the management of gradle in Android; and with ARouter, jump and switch at will

Preface : This modular and componentized article will be introduced in two articles. Some people may think that there are already articles on the Internet, so why write them. First: In order to record one's own normal, it can be regarded as a note. Second: Although there are good articles on the Internet, I recently read an article that has more than 150 likes, but the introduction is vague and many knowledge points are omitted. The focus of this article is to let you get started quickly, understand and use.

This modular/componentization explanation is divided into 2 articles (you must first understand ARouter, or a third-party routing framework):
1. The basic use of the Alibaba routing framework ARouter
2. The componentization of Android through the management of gradle; and cooperate with ARouter , Jump and switch at will

In fact, before I knew about modularity/componentization, I thought it was very high-end, and I didn't even dare to touch it. Maybe it's because other people's blogs are very high-end. In fact, after contact, I found that it was actually achieved through gradle management. The high-end is just the communication between modules, but ARouter has helped us solve everything. Follow me step by step. Realize together, add this demo at the end.

Before the start of this article
, let me talk about a little knowledge : A module introduces B module, and B module introduces C module. If the implementation method is used, then C is invisible to A; and the api method C is used. Can be accessed by A. In the same way, the conclusion of replacing C with open source libraries, so files, aar files, and jar files is also applicable.
If it is a jar package, if the parent class wants to introduce the sub-class, it must add road strength such as: dirs'libs','.../moduleB/libs'. The parent class introduces libs in moduleB

1. The demo and final effect of this article

This is the same as most online, taking WeChat as an example. Divide WeChat into 4 modules: home, chat, search, mine.

  • For the main program app, these 4 modules are its library. Generate our main app after running the app;

By changing the configuration of gradle, we can separately run the home module, chat module, search module, and mine module as an app. The advantage of this modularity is decoupling. The modules are developed by developers without affecting each other, and code management will not conflict, etc.

The final result is this, here I only wrote the home module, the chat module. In fact, the other two are the same. (And this is a project, generated by gradle)

These apps can be generated just by changing the configuration of gradle.

Complete app home_module runs the app separately chat_module run app separately
From the outermost layer

2. Create a new project and use config.gradle to manage the version number to avoid version conflicts

Create a new project, my project here is CatModuleStu, in the directory of the project build.gradle, a new config.gradle. If you don’t, copy the project build.gradle directly and change the name. The content inside is changed according to the build.gradle in our app. The build.gradle in the app is as follows:

apply plugin: 'com.android.application'

android {
    
    
    compileSdkVersion 28
    defaultConfig {
    
    
        applicationId "com.lihang.catmodulestu"
        minSdkVersion 22
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }
    buildTypes {
    
    
        release {
    
    
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    
    
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}


It means to express these specific references with constants, and each module uses these constants , so our confing.gradle is as follows:

ext {
    
    
    //这里是配置开关。是否需要单独运行。注意,这里只能打开一个。因为一次只能运行一个。。
    //true 表示需要单独运行。false表示不需要单独运行。
    isNeedHomeModule = false
    isNeedChatModule = false
    isNeedFindModule = false
    isNeedMineModule = false

    android = [
            compileSdkVersion: 28,
            buildToolsVersion: "28.0.0",
            minSdkVersion    : 22,
            targetSdkVersion : 28,
            versionCode      : 1,
            versionName      : "1.0.0",
            applicationId    : "com.lihang.catmodulestu",
            applicationHomeId: "com.lihang.homemodule",
            applicationChatId: "com.lihang.chatmodule"
    ]

    //这个对依赖库版本version的管理,就更加细致化了
    version = [
            androidSupportSdkVersion: "28.0.0"
    ]

    //系统依赖
    dependencies = [
            "support:appcompat-v7": "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}",
            "test:runner"         : 'com.android.support.test:runner:1.0.2',
            "test.espresso"       : 'com.android.support.test.espresso:espresso-core:3.0.2',
            "junit"               : 'junit:junit:4.12'
    ]

    //第三方库(请原谅我的英语)
    //这样依赖库看起来比较清晰(dependencies : 代表系统依赖库;thridencies代表第三依赖库)
    thridencies = [
            "butterknife"         : 'com.jakewharton:butterknife:8.8.1',
            "butterknife-compiler": 'com.jakewharton:butterknife-compiler:8.8.1',
            "arouter-compiler"    : 'com.alibaba:arouter-compiler:1.1.4',
            "arouter"             : 'com.alibaba:arouter-api:1.3.1',
    ]
}


After these are written, at the top of our project build.gradle, quote our config.gradle :
apply from: "config.gradle"


After doing this, look at the build.gradle of our app

apply plugin: 'com.android.application'

android {
    
    
    compileSdkVersion rootProject.ext.android["compileSdkVersion"]
    defaultConfig {
    
    
        applicationId rootProject.ext.android["applicationId"]
        minSdkVersion rootProject.ext.android["minSdkVersion"]
        targetSdkVersion rootProject.ext.android["targetSdkVersion"]
        versionCode rootProject.ext.android["versionCode"]
        versionName rootProject.ext.android["versionName"]
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
    
    
        release {
    
    
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    
    
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation rootProject.ext.dependencies["support:appcompat-v7"]
    testImplementation rootProject.ext.dependencies["junit"]
    androidTestImplementation rootProject.ext.dependencies["test:runner"]
    androidTestImplementation rootProject.ext.dependencies["test.espresso"]
}

3. Create a new baseModule.

Create a new baseModule, put commonly used network requests, image loading, which means that things shared by other modules are put in. Including shared resource files, BaseActivity, BaseFragment and application are also placed here, the use of ARouter mentioned in the previous article, initialization is also placed This. This baseModule is completely used as a library. Referenced by other modules and apps. But there are two pitfalls here :
1. When ARouter is introduced, add under the dependencies tag in baseModule's build.gradle:

api rootProject.ext.thridencies["arouter"]
annotationProcessor rootProject.ext.thridencies["arouter-compiler"]

Add to the android defaultConfig tag

javaCompileOptions {
    
    
            annotationProcessorOptions {
    
    
                arguments = [moduleName: project.getName()]
            }
        }

When other modules refer to baseModule, javaCompileOptions should also be added under the defaultConfig tag. At the same time, the following dependencies must be introduced, right! You read it right. If it is not added, then it will be unsuccessful. :

annotationProcessor rootProject.ext.thridencies["arouter-compiler"]

2. Butterknife is used here . For example, after baseModule references butterknife, when other modules reference baseModule, a bug or error message will appear: the element must be a constant. Then butterknife officially provides a solution, using R2. But when the module is switched to run as an app, R2 will report an error, meaning that R2 should be changed to R when used as an app. If you use R2 in your module, you must change it to R at this time. Therefore, it is not recommended that baseModule reference butterknife. If there is a good solution, please let me know! !


4. Create a new homeModule

First look at the build.gradle of homeModule:

//这里就用到了config的配置isNeedHomeModule
//开头设置,如果打开开光,当成项目运行,否则当成library引用
if (isNeedHomeModule.toBoolean()) {
    
    
    apply plugin: 'com.android.application'
} else {
    
    
    apply plugin: 'com.android.library'
}

android {
    
    
    compileSdkVersion rootProject.ext.android["compileSdkVersion"]

    defaultConfig {
    
    
        minSdkVersion rootProject.ext.android["minSdkVersion"]
        targetSdkVersion rootProject.ext.android["targetSdkVersion"]
        if (isNeedHomeModule.toBoolean()) {
    
    
            //同时在conifg.gradle配置上homeModule的包名。
            //当作为application运行的时候,给他配置上独立的包名
            applicationId rootProject.ext.android["applicationHomeId"]
        }
        versionCode rootProject.ext.android["versionCode"]
        versionName rootProject.ext.android["versionName"]
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        //ARouter的使用记得要加哦
        javaCompileOptions {
    
    
            annotationProcessorOptions {
    
    
                arguments = [moduleName: project.getName()]
            }
        }
    }

    buildTypes {
    
    
        release {
    
    
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    sourceSets {
    
    
        main {
    
    
            if (isNeedHomeModule.toBoolean()) {
    
    
                //这里目前的做法是2套AndroidManifest,作为app运行的时候要指定启动页
                manifest.srcFile 'src/main/buildApp/AndroidManifest.xml'
            } else {
    
    
                manifest.srcFile 'src/main/buildModule/AndroidManifest.xml'
            }
        }
    }

}

dependencies {
    
    
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation rootProject.ext.dependencies["support:appcompat-v7"]
    testImplementation rootProject.ext.dependencies["junit"]
    androidTestImplementation rootProject.ext.dependencies["test:runner"]
    androidTestImplementation rootProject.ext.dependencies["test.espresso"]
    implementation project(':baseModule')
    annotationProcessor rootProject.ext.thridencies["arouter-compiler"]
}

From the code above, we can see that there are 3 points to note :

  • 1. Judge whether to run as an app or a library through isNeedHomeModule
if (isNeedHomeModule.toBoolean()) {
    
    
    apply plugin: 'com.android.application'
} else {
    
    
    apply plugin: 'com.android.library'
}

  • 2. Judge by isNeedHomeModule, if running as an app, configure appId for it
    if (isNeedHomeModule.toBoolean()) {
    
    
            applicationId rootProject.ext.android["applicationHomeId"]
        }

  • 3. Judging by isNeedHomeModule, configure two AndroidManifests, one with a startup page and the other without a startup page.
if (isNeedHomeModule.toBoolean()) {
    
    
                //这里目前的做法是2套AndroidManifest,作为app运行的时候要指定启动页
                manifest.srcFile 'src/main/buildApp/AndroidManifest.xml'
            } else {
    
    
                manifest.srcFile 'src/main/buildModule/AndroidManifest.xml'
            }

The two AndroidManifests are not posted here, and you can see them by yourself through my demo. In fact, it is to specify a startup page Activity. For the clarity of the demo, I created a SelectHomeActivity, and then the homeModule module, and all the activity configurations used are placed here. If it is used as a library, put all the configuration here. When quoted by the main app, he will automatically come here to find it, so we don’t need to worry about it.

After doing this, when homeModule is running as an app, our main app must of course also judge, and homeModule cannot be referenced. Build.gradle is as follows:

dependencies {
    
    
   ... //省略部分代码,便于理解
    if (!isNeedHomeModule.toBoolean()) {
    
    
        implementation project(':homeModule')
    }
}

After doing this, you are done. Other modules have the same configuration as this one. By changing the configuration isNeedHomeModule in config.gradle, modules can be run separately.

5. When each module class is used in the main app.

As a library reference. For example, my demo is to switch by clicking the button below. Because we use ARouter, it can be like this (of course it is quoted here, you can also use the class name directly):

//这样就生成了一个HomeFragment的实例
HomeFragemnt fragment_one = (HomeFragemnt) ARouter.getInstance().build(Constance.FRAGMENT_HOME_PATH).withString("wo", "1").navigation();

Conclusion: So far, about modularization and componentization in android, that's it

Here I will talk about my own understanding:
Modularization: Just like this, we divide our WeChat into 4 modules. I understand it as modularity.

Componentization: Just like the baseModule here, or the dialog or popwindow you encapsulate. For another example, some effects of the third-party online you use. I understand it as modularity. Like modularity, it is for decoupling.

Your likes are my biggest motivation, github address

Guess you like

Origin blog.csdn.net/leol_2/article/details/100700132