前言:这篇模块化与组件化的文章,用2篇文章介绍。可能有些人以为网上已经有了文章,为何还要写。第一:为了记录本身的正常也算当作笔记。第二:网上当然有好文,但最近看了一篇竟然有150多赞,可是介绍的迷迷糊糊,不少知识点略过。本文重点是让你快速入门,理解以及使用。java
本次模块化/组件化讲解总共分2篇(必须先了解ARouter,或第三方路由框架):
一、阿里路由框架ARouter的基本使用
二、Android中经过对gradle的管理实现组件化;并配合ARouter,随意跳转切换android
其实在没了解过模块化/组件化以前,我以为很是高端,甚至不敢触碰。多是由于其余人的博客很高端。其实接触后发现,其实就是经过gradle的管理实现的。高端的只不过是各模块module之间的通讯,但是ARouter已经帮咱们解决了全部事情。跟着我一步一步。一块儿实现,末尾加上本文demo。git
在本文文章开始前,先讲个小知识点:
A module 引入了B module,B module引入了C module,若是使用的是implementation方式,那么C对于A来讲是不可见的;而使用api方式C是能够被A访问的。同理,把C换成开源库、so文件、aar文件、jar包文件结论也适用。 若是是jar包的,父类要引入子类的话必需要加上路劲如: dirs 'libs', '../moduleB/libs' 。父类引入moduleB中的libsgithub
这里也和大部分网上同样,以微信为例。把微信分为4个模块module:home、chat、search、mine。api
经过对gradle配置开光的更改,咱们能够单独把home模块,chat模块,search模块,mine模块,自身做为app运行。这样模块化的好处是,解耦。把各模块给开发人员开发,互不影响,且代码管理也不会起冲突等等。微信
这些app的生成,只是改变gradle的配置开光便可生成。网络
完整app | home_module单独运行app | chat_module单独运行app |
---|---|---|
![]() |
![]() |
![]() |
从最外层看 | ||
![]() |
新建项目,我这里的项目是CatModuleStu,在项目build.gradle的目录下,new一个config.gradle。不会的直接复制项目build.gradle,改下名字便可。里面的内容呢,根据咱们的app里build.gradle进行更改,app里的build.gradle以下:app
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' } 复制代码
意思就是把这些具体的引用用常量表示,每一个module都使用这些常量,因此咱们的confing.gradle以下:框架
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',
]
}
复制代码
这些写好以后,在咱们的项目build.gradle最顶部,引用一下咱们的config.gradle: apply from: "config.gradle"ide
作完这些后,在来看咱们的app的build.gradle
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"] } 复制代码
新建baseModule,把经常使用的网络请求,图片加载,意思就是其余module共用的东西放进去.包括共用的资源文件,BaseActivity,BaseFragment和application也放在这,上篇说的ARouter的使用,初始化也放在这。这个baseModule是彻底做为library的。被其余module和app引用。可是这里有2个坑:
一、引入ARouter的时候,在baseModule的build.gradle里dependencies标签下加上:
api rootProject.ext.thridencies["arouter"]
annotationProcessor rootProject.ext.thridencies["arouter-compiler"]
复制代码
在android的defaultConfig标签加上
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
复制代码
其余module引用baseModule的时候,一样也要在defaultConfig标签下加上javaCompileOptions。同时还要引入以下依赖,对!你没有看错。若是不加,那么将不成功。:
annotationProcessor rootProject.ext.thridencies["arouter-compiler"]
复制代码
二、这里用butterknife。好比baseModule引用了butterknife后,其余module引用baseModule的时候会出现个bug、错误提示:元素必须为常量。而后butterknife官方提供了一个处理方案,用R2。可是在切换module做为app运行的时候,R2会报错,意思做为app的时候R2要改为R。假如你module里都用了R2,这个时候要所有改为R。因此不建议baseModule引用butterknife。若有好的解决方法,望告知!!
首先看homeModule的build.gradle:
//这里就用到了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"] } 复制代码
从上代码中咱们能够看到有3点注意的地方:
if (isNeedHomeModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
复制代码
if (isNeedHomeModule.toBoolean()) {
applicationId rootProject.ext.android["applicationHomeId"]
}
复制代码
if (isNeedHomeModule.toBoolean()) {
//这里目前的作法是2套AndroidManifest,做为app运行的时候要指定启动页
manifest.srcFile 'src/main/buildApp/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/buildModule/AndroidManifest.xml'
}
复制代码
这里2个AndroidManifest就不贴了,能够经过个人demo本身去看。其实就是指定一个启动页Activity。我这里为了demo的清晰,就新建了个SelectHomeActivity,而后就是homeModule模块,用到的全部Activity配置什么的都放在这里。若是是做为library,也是把全部的配置放在这。被主app引用的时候,他会自动来这里找的,不用咱们担忧。
作完这些,当homeModule做为app运行的时候,咱们主app固然也要判断,就引用不了homeModule了,build.gradle以下:
dependencies {
... //省略部分代码,便于理解
if (!isNeedHomeModule.toBoolean()) {
implementation project(':homeModule') } } 复制代码
作完这些,就完成了。其余module也是和这个一样的配置。经过更改config.gradle的配置isNeedHomeModule,就能够模块单独之间运行了。
当成library引用后。好比我这个demo是点击下方按钮进行切换。由于咱们使用了ARouter,就能够这样(固然这里引用,你也能够直接用类名):
//这样就生成了一个HomeFragment的实例
HomeFragemnt fragment_one = (HomeFragemnt) ARouter.getInstance().build(Constance.FRAGMENT_HOME_PATH).withString("wo", "1").navigation();
复制代码
这里我说个我本身的理解:
模块化:就像这样,把咱们的“微信”,分为4个模块。我理解为模块化。
组件化:就像这里的baseModule,或者你封装的dialog,popwindow。再好比,你用的网上第三方的一些效果。我理解为模块化。和模块化同样,都是为了解耦。