一、模块化、组件化与插件化
项目发展到一定程度,随着人员的增多,代码越来越臃肿,这时候就必须进行模块化的拆分。在我看来,模块化是一种指导理念,其核心思想就是分而治之、降低耦合。而在Android工程中如何实施,目前有两种途径,也是两大流派,一个是组件化,一个是插件化。
提起组件化和插件化的区别,有一个很形象的图:
组件化和插件化对比.png
上面的图看上去比较清晰,其实容易导致一些误解,有下面几个小问题,图中可能说的不太清楚:
·组件化是一个整体吗?去了头和胳膊还能存在吗?左图中,似乎组件化是一个有机的整体,需要所有器官都健在才可以存在。而实际上组件化的目标之一就是降低整体(app)与器官(组件)的依赖关系,缺少任何一个器官app都是可以存在并正常运行的。
·头和胳膊可以单独存在吗?左图也没有说明白,其实答案应该是肯定的。每个器官(组件)可以在补足一些基本功能之后都是可以独立存活的。这个是组件化的第二个目标:组件可以单独运行。
·组件化和插件化可以都用右图来表示吗?如果上面两个问题的答案都是YES的话,这个问题的答案自然也是YES。每个组件都可以看成一个单独的整体,可以按需的和其他组件(包括主项目)整合在一起,从而完成的形成一个app
·右图中的小机器人可以动态的添加和修改吗?如果组件化和插件化都用右图来表示,那么这个问题的答案就不一样了。对于组件化来讲,这个问题的答案是部分可以,也就是在编译期可以动态的添加和修改,但是在运行时就没法这么做了。而对于插件化,这个问题的答案很干脆,那就是完全可以,不论实在编译期还是运行时!
·本文主要集中讲的是组件化的实现思路,对于插件化的技术细节不做讨论,我们只是从上面的问答中总结出一个结论:组件化和插件化的最大区别(应该也是唯一区别)就是组件化在运行时不具备动态添加和修改组件的功能,但是插件化是可以的。
·暂且抛弃对插件化“道德”上的批判,我认为对于一个Android开发者来讲,插件化的确是一个福音,这将使我们具备极大的灵活性。但是苦于目前还没有一个完全合适、完美兼容的插件化方案(RePlugin的饥饿营销做的很好,但还没看到疗效),特别是对于已经有几十万代码量的一个成熟产品来讲,套用任何一个插件化方案都是很危险的工作。所以我们决定先从组件化做起,本着做一个最彻底的组件化方案的思路去进行代码的重构,下面是最近的思考结果,欢迎大家提出建议和意见。
以上是专业的解答,我看后的理解是,组件化就是将app划分为多个独立的功能,每个功能都能通过调试安装在手机上。每个独立的组件是互不影响的,当所有组件合并在一起就是一个完整的app。这种组件化的方式适合多人协同开发,互不影响,而且进度上也不用等其他人完成。
二、如何实现组件化
1.代码解耦使用android studio在程序中创建module目录,每个module用library的样式创建。分为两种类型,一种是library依赖库,将app公共部分的类,参数和第三方库放在里面,另一种component是一个完整功能模块,也就是我们将功能组件化后的产物。
2.组件的单独调试
上面说过,每个组件分离出来后都是一个完整的功能模块,是可以进行调试安装的。首先需要在每个模块下面的gradle.properties中定义变量isRunAlone = true,在build.gradle头部加入:
if(isRunAlone.toBoolean()){
apply plugin: 'com.android.application' //isRunAlone为true时,进行单独开发调试
}else{
apply plugin: 'com.android.library' //isRunAlone为false时,合并成一个项目
}
在app项目下也需要定义gradle.properties中定义变量isRunAlone = true,在build.gradle的dependencies标签下加入:
if (!isRunAlone.toBoolean()){
compile project(':imagecomponent') //isRunAlone为false时,将组件加入到应用中
compile project(':textcomponent')
}
3.组件间的调用和传输
组件间的调用可以有好几种方式,这里我讲下阿里巴巴的页面路由框架Arouter,它的集成可以有效的解决调用和数据传输。https://github.com/alibaba/ARouter github网址
典型应用
从外部URL映射到内部页面,以及参数传递与解析
跨模块页面跳转,模块间解耦
拦截跳转过程,处理登陆、埋点等逻辑
跨模块API调用,通过控制反转来做组件解耦
我主要讲下基础功能:
1.添加依赖和配置
dependencies {
//替换成最新版本,需要注意的是api
//要与compiler匹配使用,均使用最新版可以保证兼容
compile 'com.alibaba:arouter-api:x.x.x'
annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
...
}
2.添加注解
在你需要调用的组件入口类的顶部加入注解:
//在支持路由的页面上添加注解(必选)
//这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity {
...
}
3.初始化SDK
一般在app项目的application的oncreate方法中初始化。
if (isDebug()) { //这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog(); //打印日志
ARouter.openDebug(); //开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(mApplication); //尽可能早,推荐在Application中初始化
4.发起路由操作
// 1.应用内简单的跳转(通过URL跳转在'进阶用法'中)
ARouter.getInstance().build("/test/activity").navigation();
// 2.跳转并携带参数
ARouter.getInstance().build("/test/1")
.withLong("key1", 666L)
.withString("key3", "888")
.withObject("key4", new Test("Jack", "Rose"))
.navigation();
接收带参数的跳转使用getIntent.getString(“key3”,””);这种方式接收。
注意:各模块下build.gradle的defaultConfig标签下需加入:
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
网友评论