一个项目,随着业务的发展,模块会变的愈来愈多,代码量也会变的异常庞大,进而可能开发的人会愈来愈多,这种状况下若是仍是基于单一工程架构,那就须要每个开发者都熟悉全部的代码,并且代码之间耦合严重,一个模块穿插着大量其余业务模块的逻辑,严重的话可能使项目处于牵一发而动全身,不想轻易修改的局面;并且庞大的单一工程项目会致使编译速度极慢,开发者长时间等待编译结果,很是不利于开发工做。因此,就须要一个灵活的架构来解决这些问题,组件化架构思想应运而生。java
针对上面所说的几个问题,下面咱们逐个说明它们的解决方案,当解决完这些问题,你会发现,你已经搭建了一个基于组件化的项目。下图是一个完整的组件化项目结构:common是基础组件module,做为library存在,须要全部组件依赖;comp一、comp2做为组件存在,可配置成library或可独立运行的module;app是个壳,经过组装组件实现其价值。android
Android工程经过gradle构建,经过配置每一个module的gradle,来实现module的不一样表现。Android Studio的module有两种属性,分别是:api
module属性经过其目录下的gradle文件配置,当module属性为application时,该module做为完整的app存在,能够独自运行,方便编译和调试;当module属性为library时,该module做为一个依赖库,被壳工程依赖并组装成一个app。那么如何让这两种模式能够自动转换呢?若是每次切换模式的时候,都手动去修改每一个组件的配置,组件少的状况下还能够接受,组件多了会很是不方便,下面就让咱们来聊聊如何实现两种模式的自动转换。网络
buildscript {
ext.kotlin_version = '1.3.21'
ext.isModule = true //true-每一个组件都是单独的module,可独立运行 false-组件做为library存在
repositories {
google()
jcenter()
}
}
复制代码
//1
if (rootProject.ext.isModule) {
//可独立运行的app
apply plugin: 'com.android.application'
} else{
//被依赖的library
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
//applicationId "com.study.comp1" //2 若是没有,默认包名为applicationId
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
//3
sourceSets {
main {
if(rootProject.ext.isModule){
manifest.srcFile 'src/main/java/module/AndroidManifest.xml'
} else{
manifest.srcFile 'src/main/java/library/AndroidManifest.xml'
java {//移除module包下的代码
exclude 'module'
}
}
}
}
}
复制代码
上面是截取的组件gradle的部分代码,包含了组件化须要配置的全部内容,每一点都进行了注释架构
在组件化架构中,不一样的组件之间是平衡的,不存在相互依赖的关系(可参考文章开头的架构图)。所以,假设在组件A中,想要跳转到组件B中的页面,若是使用Intent显式跳转就行不通了,并且你们都知道,Intent隐式跳转管理起来很是不方便,因此Arouter出现了,而且有强大的技术团队支持,能够放心使用了。那么如何在组件化架构中应用Arouter呢?下面详细来聊一聊app
在common组件中将Arouter依赖进来,并配置编译参数;在业务组件中引入arouter编译器插件,同时配置编译器参数,下面是Common组件gradle文件的部分片断ide
//配置arouter编译器参数,每一个组件都需配置
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
dependencies {
//arouter api,只需在common组件中引入一次
api('com.alibaba:arouter-api:1.4.1') {
exclude group: 'com.android.support'
}
//arouter编译器插件,每一个组件都需引入
kapt 'com.alibaba:arouter-compiler:1.2.2'
}
复制代码
在组件架构中,常常会遇到组件须要使用全局Context的状况,当组件属性为app时,能够经过自定义Application实现;当组件属性为library时,因为组件被app依赖,致使没法调用app的Application实例,并且自身不存在Application;因此,这里给出的方案是在common组件中建立一个BaseApplication,而后让集成模式(组件模式)下的Application继承BaseApplication,在BaseApplication中获取全局Context,并作一些初始化的工做,这里须要初始化Arouter,以下是在common组件中声明的BaseApplication工具
abstract class BaseApplication : Application() {
companion object {
var _context: Application? = null
//获取全局Context
fun getContext(): Application {
return _context!!
}
}
override fun onCreate() {
super.onCreate()
_context = this
//初始化Arouter
initARouter()
//初始化其余第三方库
}
private fun initARouter() {
if (BuildConfig.DEBUG) {
ARouter.openDebug()
ARouter.openLog()
}
ARouter.init(this)
}
override fun onTerminate() {
super.onTerminate()
//清理Arouter注册表
ARouter.getInstance().destroy()
}
}
复制代码
根据Arouter的路由特性,初始化以后,就能够经过@Route注解注册页面,而后调用Arouter api实现页面的跳转了(这里所谓的跨组件页面跳转是指在集成模式下,而非组件模式下),无关乎是否在同一个组件下面, 假设咱们要从组件1页面携带参数跳转到组件2页面,请看下面示例组件化
/**
* 在组件2中经过@Route注解注册该页面
*/
@Route(path = "/comp2/msg",name = "我是组件2的MSGActivity")
class Comp2MsgActivity : BaseActivity() {
//传递过来的参数
@Autowired(name = "msg")
@JvmField
var msg: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//注入传递的参数
ARouter.getInstance().inject(this)
setContentView(R.layout.comp2_activity_msg)
comp2_msg_msg.text = msg!!
}
}
//在组件1中发起跳转命令
ARouter.getInstance()
.build("/comp2/msg")
.withString("msg", "hello Im from Comp1")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.navigation()
复制代码
以上便完成了一次简单的跨越组件的页面跳转,仅仅是Arouter的基本使用而已。解决了组件间页面跳转的问题后,咱们来看看组件之间通讯、调用彼此服务的实现。gradle
组件间通讯功能和路由功能有着共通的地方,即都是利用Arouter的基础功能实现,在Arouter驱动层定义各个组件对外提供的接口,而后在组件自身模块实现该接口,经过Arouter调用其余组件服务。假设咱们在组件2中须要调用组件1中的服务,能够总结为如下3点
/**
* 组件1对外提供的接口
*/
interface CompServer1 : IProvider {
fun showMsg(msg: String)
}
复制代码
@Route(path = "/comp1/server",name = "comp1对外提供的服务")
class CompServer : CompServer1 {
var mContext: Context? = null
override fun showMsg(msg: String) {
Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show()
}
override fun init(context: Context?) {
this.mContext = context!!
}
}
复制代码
val server1 = ARouter.getInstance().build("/comp1/server").navigation() as CompServer1
server1.showMsg("我从comp2吊起了comp1的接口")
复制代码
有没有感受很简单??没错,就是这么简单,赶忙去用吧!哈哈
说到混淆,有人可能会疑惑,若是在各个组件中混淆可不能够?不建议这样混淆!!由于组件在集成模式下被gradle构建成了release类型的aar包,若是在组件中进行混淆,一旦代码出现了bug,这个时候就很难根据日志去追踪bug产生的缘由,并且不一样组件分别进行混淆很是不方便维护和修改,这也是不推荐在业务组件中配置buildType(构建类型)的缘由。
因此,组件化项目的代码混淆放在集成模式下的app壳工程,各个业务组件不配置混淆。集成模式下在app壳工程.gradle文件的release构建模式下开启混淆,其余buildType配置和普通项目相同,混淆文件放在app壳工程下,各个组件的代码混淆均放在该混淆文件中。
以上,咱们已经逐一解决了组件化所面对的各个问题,至此,咱们已经搭建了一个简单的组件化架构的项目,这一块儿感受是在不知不觉中就实现了,并非很难哦!如今,咱们总结一下组件化的优点了