这篇文章主要是记录下模块化的搭建,当然也有路由的使用,Arouter虽然被业界所推宠,但Arouter真的还是有很多坑,这里我要说一下,模块化开发只适合多人、项目较大的时候使用,如果你满足不了这两个条件,最好不要用,个人觉得没有什么意义,闲话少说,我们切入正题。
模块化:(个人理解)就是把你项目的业务模块划分为若干模块,比如:微信主页有4个tab页面:可以划分4个模块,当然这只是举个例子,如果你完全可以划分的更细。
组件化:(个人理解)就是一些工具库,如:支付、分享、封装的网络库,这些都是一个一个的组件。
为了便于理解,我把demo的主页效果图先贴出来:
这个页面一共三个tab,我分了三个模块,分别是chat、contact、me
接着看下,我的项目截图:
红线框里面就是上面提到的三个模块,当然还有其他的,我们一一介绍下:
- app:就是一个空壳子,但是我们会在这里配置我们集成模式的application,一个app只有一个application这个大家都明白,那么我就在这个空app模块里配置它
- common_module:在我看来它是一个组件,所有模块公用的组件,因为我会把一个基类放到这个组件里,当我用到时候再去依赖它
- entrance_module:入口模块,比如我们的开屏广告页面或则宣传页
- main_module:就是我们的主页activity,我把它单独当到了一个模块,其实这样意义不大(反而会增加代码编译打包的时间),主要是为了说明问题
接着我们开始改写build.gradle、gradle.properties文件来构建模块化:
我们打开项目根目录gradle.properties文件,加入变量,这个变量就是我们用来判断是集成模式,还是单模块模式:
#true:单模块模式 false:集成模式
isModule=false
接着我们去空壳app模块,在清单文件里加上我们的application,接着去app的build.gradle文件中添加:
dependencies {
//这里是组件库,每个模块都会用到
implementation project(':common_module')
//这里判断是集成模式还是单模块调试模式
if (!isModule.toBoolean()) {
implementation project(':entrance_module')
implementation project(':main_module')
implementation project(':chat_module')
implementation project(':mine_module')
}
}
大家注意看isModule.toBoolean()在这里用到了刚刚我们定义的变量,如果集成模式,它会其他业务模块当成library依赖到app,从而来编译打包成一个完整的apk,如果是单独调试模式,也就不会依赖,每个模块都可以当做一个完整的apk,来打包测试。
那么现在空壳app做好了,我们只要在实现一个模块就表示我们的搭建完成了,新建module,选android library,开始配置我们的模块(以entrance_module为例):
你一定很好奇,为什么要配置2个清单文件?没错!就是为什么单独调试用的,你所看到的的module文件夹下的清单文件就是我们用来单模块调试的使用,因为它需要一个单独的application,所以我们就在debug文件夹下定义了一个application,那我怎么知道当前是调试模式还是集成模式呢?,当然是在build.gradle文件里判断啦,
看代码:
if (isModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
sourceSets {
main {
if (isModule.toBoolean()) {
//这里是单模块调试模式加载的清单文件
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
exclude 'debug/**'
}
}
}
}
}
dependencies {
implementation project(':common_module')
}
是不是很简单?这样我们的模块化就搭建完成了,以为其他模块也是这样配置的~
让我们修改gradle.properties定义的变量,调到单模块模式看下:
是不是每个模块都是可安装的?这样就成功!
当然你要是想单模块调试,你要先定义一个启动的activity,然后在你的module文件夹下的清单文件中配置入口过滤器,这样就可以了。
我们看下chat_module这个模块单独调试界面:
再看我们的集成模式,一样修改gradle.properties变量:
是不是我们的模块都变成了library形式,而且只有app可安装!
来看下我们集成后的效果:
这时候你会发现手机上的单独模块的apk和集成模式的apk共存(看下图):
你一定发现在entrance_module模块中跳转报错,跳转失败,那就是arouter路由。
下面说下arouter的配置和简单使用跳转:
先说配置:
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
//kotlin注解插件
apply plugin: 'kotlin-kapt'
//这里一定要加,获取module的名字
kapt {
arguments {
arg("moduleName", project.getName())
}
}
dependencies {
//这个我抽取出来了
api rootProject.ext.dependencies.arouter_api
kapt 'com.alibaba:arouter-compiler:1.1.4'
}
因为我用的是kotlin语言开发,所以注解插件也是kapt,还有一个 api和implementation我要说明下,以为有些库引用,你每个module都要引用,所以不如抽取规范化,让common_module去依赖,然后其他模块依赖common_module,这样统一管理依赖库的版本,这时候你就会发现common_module依赖的方式如果是implementation,那么其他依赖common_module的模块就不能使用这些库,只有api可以。
我的统一库放到了根目录下build.gradle文件下:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.2.41'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
ext {
android = [
compileSdkVersion: 27,
minSdkVersion : 15,
targetSdkVersion : 27,
versionCode : 1,
versionName : "1.0"
]
versions = [
kotlin_version = "1.2.41",
appcompat_version = "27.1.1",
// constraint_layout_version = "1.1.0",
design_version = "27.1.1",
arouter_api_version = "1.3.1",
retrofit_version = "2.4.0",
converter_gson = "2.4.0",
adapter_rxjava2 = "2.4.0",
gson_version = "2.8.5",
rxkotlin_version = "2.2.0",
rxandroid_version = "2.0.2",
design_version = "27.1.1",
support_v4_version = "27.1.1",
SmartRefreshLayout_version="SmartRefreshLayout"
]
dependencies = [
appcompat_v7 : "com.android.support:appcompat-v7:$rootProject.appcompat_version",
kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jre7:$rootProject.kotlin_version",
// constraint_layout: "com.android.support.constraint:constraint-layout:$rootProject.constraint_layout_version",
design : "com.android.support:design:$rootProject.design_version",
arouter_api : "com.alibaba:arouter-api:$rootProject.arouter_api_version",
retrofit : "com.squareup.retrofit2:retrofit:$retrofit_version",
converter_gson : "com.squareup.retrofit2:converter-gson:$converter_gson",
adapter_rxjava2: "com.squareup.retrofit2:adapter-rxjava2:$adapter_rxjava2",
gson : "com.google.code.gson:gson:$gson_version",
rxkotlin : "io.reactivex.rxjava2:rxkotlin:$rxkotlin_version",
rxandroid : "io.reactivex.rxjava2:rxandroid:$rxandroid_version",
design : "com.android.support:design:$design_version",
support_v4 : "com.android.support:support-v4:$support_v4_version",
SmartRefreshLayout: "com.scwang.smartrefresh:SmartRefreshLayout:$SmartRefreshLayout_version"
]
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
接着我们看下arouter路由的简单使用:
当然你要现在app中初始化:
/**
* @Author QLY
* @Date 2018/6/14
* @Desc Arouter路由初始化
*/
class EntranceApp : BaseApplication(){
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
ARouter.openLog()
ARouter.openDebug()
}
ARouter.init(this)}
}
/**
* @Author QLY
* @Date 2018/6/14
* @Desc Arouter路由统一跳转
*/
class RouteUtils {
companion object {
const val CHAT_FRAGMENT = "/chat/ChatFragment"
const val CONTACT_FRAGMENT = "/entrance/ContactFragment"
const val MINE_FRAGMENT = "/mine/MineFragment"
fun routerFragment(path: String): Fragment {
return ARouter.getInstance().build(path).navigation() as Fragment
}
const val MAIN_ACTIVITY = "/main/MainActivity"
fun routerActivity(path:String){
ARouter.getInstance().build(path).navigation()
}
}
}
/**
* @Author QLY
* @Date 2018/6/14
* @Desc 入口界面
*/
class EntranceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_entrance)
button.setOnClickListener {
//调用
RouteUtils.routerActivity(RouteUtils.MAIN_ACTIVITY)
}
}
}
/**
* @Author QLY
* @Date 2018/6/14
* @Desc 主界面
*/
//接收
@Route(path = RouteUtils.MAIN_ACTIVITY)
class MainActivity : AppCompatActivity()