这篇文章主要是记录下模块化的搭建,当然也有路由的使用,Arouter虽然被业界所推宠,但Arouter真的还是有很多坑,这里我要说一下,模块化开发只适合多人、项目较大的时候使用,如果你满足不了这两个条件,最好不要用,个人觉得没有什么意义,闲话少说,我们切入正题。


模块化:(个人理解)就是把你项目的业务模块划分为若干模块,比如:微信主页有4个tab页面:可以划分4个模块,当然这只是举个例子,如果你完全可以划分的更细。
组件化:(个人理解)就是一些工具库,如:支付、分享、封装的网络库,这些都是一个一个的组件。

为了便于理解,我把demo的主页效果图先贴出来:

模块化架构 大型系统 模块化搭建_ci

这个页面一共三个tab,我分了三个模块,分别是chat、contact、me

接着看下,我的项目截图:

模块化架构 大型系统 模块化搭建_ci_02


红线框里面就是上面提到的三个模块,当然还有其他的,我们一一介绍下:

  • 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为例):

模块化架构 大型系统 模块化搭建_集成模式_03


你一定很好奇,为什么要配置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定义的变量,调到单模块模式看下:

模块化架构 大型系统 模块化搭建_集成模式_04

是不是每个模块都是可安装的?这样就成功!

当然你要是想单模块调试,你要先定义一个启动的activity,然后在你的module文件夹下的清单文件中配置入口过滤器,这样就可以了。

我们看下chat_module这个模块单独调试界面:

模块化架构 大型系统 模块化搭建_模块化架构 大型系统_05

再看我们的集成模式,一样修改gradle.properties变量:

模块化架构 大型系统 模块化搭建_模块化架构 大型系统_06


是不是我们的模块都变成了library形式,而且只有app可安装!

来看下我们集成后的效果:

模块化架构 大型系统 模块化搭建_ci_07

这时候你会发现手机上的单独模块的apk和集成模式的apk共存(看下图):

模块化架构 大型系统 模块化搭建_集成模式_08

你一定发现在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()

demo地址