参考资料:
https://www.jianshu.com/p/60c1b9ddd8ab
上一篇我们学习了ARouter,讲到ARouter是组件化开发的基础,那现在让我们开始组件化开发吧。
1.组件化,模块化概念
对于组件化的开发,首先要了解模块化及组件化的概念,这正是是好多小伙伴模糊的,所以我们有必要说明一下。
1.1 组件
组件的英文单词是component,意思是组件、部件、元件。在App工程上,件是构成业务或者功能模块的基本单位也就是组件不能被继续拆分,原则上,组件与组件之间互不依赖。比如我们的图片上传功能,可以叫图片上传组件,但不能叫图片上传模块,而且组件具有可替换性和重复利用性,可替换性指比如我们的地图定位组件可以用百度的,也可以用高德的。重复性是指我的地位功能可能在首页被调用,在其他页面也要被调用。
1.2 模块
模块的英文单词是Module,由多个组件构成。也就是从粒度上来看,模块要比组件大,也就是模块包含组件。举个例子:以安居客为例,安居客app内有二手房和新房相关功能, 这个二手房和新房就属于模块,但这二手房和新房模块都用到了分享房源的功能,而这个分享功能就属于组件了。
1.3 组件和模块的关系
我们以上图为例简单说一下,首页界面的天猫,聚划算,饿了么都属于模块同时也是一个业务,我们可以叫做业务模块,而我们继续点击天猫进入天猫超市,里面的分享功能,支付所用到的sdk等都算组件。那我们总结一下:模块化和组件化只不过是我们根据项目的需求,定义不同,一个工程可以由多个模块组成,每个模块可以由多个组件构成,模块可以单独存在,正是多个模块构成了整个项目。不论是模块化还是组件化,它们的目的都是把项目解耦便于代码的管理。高内聚、低耦合使开发人员分工明确,提高开发效率。
1.4 业务
我们上面说了模块也可以叫做业务模块,那么什么是业务呢,我们还是以上图为例:在淘宝的首页天猫,聚划算,充值中心等这些看起来完全不同的业务我们称之为Business业务。而天猫点击进入会有搜索业务,预约功能,签到等功能,聚划算点击进入也会有搜索业务,预约功能,签到等功能,像这种业务我们称之为基础业务。我们来张图说明一下Business业务和基础业务及组件之间的关系:
图中Business业务公用基础业务,而基础业务的实现可能依赖于某组件。
1.4 Library
我们刚才说了组件,比如上图提到的图片上传组件,网络组件,还有分享组件等,那这里要介绍另外一个概念:Library。你比如我们的分享组件用的是友盟分享sdk,我们的网络组件依赖的是okhttp库,这些依赖的三方库都称之为Library。
2.组件化实践
参考资料:
https://mp.weixin.qq.com/s/-gC8JpmmCZWzcOsH5ZzLtQ
https://mp.weixin.qq.com/s/8_8gGpkpO2QFNkWgSRBwIg
我准备从以下几个方面来介绍组件化:
- 代码解耦
- 组件或模块的单独运行
- 数据的传递与ui跳转
- 生命周期管理
2.1代码解耦
ok,我们先看看我们的demo实现效果,就先简单上张图吧,不整gif图片了:
你看到的第一眼可能觉得简单,没什么大不了,但是这不同于我们平时的实现方法,平时我们的首页,资讯,我的都是在app中实现,但今天我们的目录结构是这样的:
我先来说一下各个目录的含义:
- app
app模块是我们的壳工程,平时我们最核心的代码都要往这里写,但今天不一样,app模块的职能改变了,它最主要的目的是对其他模块进行整合,确保app能正常运行,里面只有一些简单的代码。 -
commonlib
commonlib 是一个依赖的Library,为什么说她是library呢?因为它的职能是把整个project所用到的library,公用的lib都放到这里,供其它模块引用,就不需要每个模块都写一遍了。以demo为例:
注意:采用api代替implementation代替的目的就是为了让其他模块能够引用到
- module_home,module_zixun,module_user,
这三个模块是整个project最核心的地方, 对应我们上面效果图中的首页,资讯,我的三个tab。
这样做的好处显而易见 :张三开发首页,李四开发资讯,王五开发我的 互不影响,提高开发效率 ,这也是组件化的优势 - x5webview
x5webview是作为组件存在的,是我基于腾讯TBS浏览服务封装的(类似webview作用),和分享组件,网路请求组件是一个级别,为了其他模块的调用。
这里就基本实现了代码的解耦,你可能有一个疑问?你这一会组件一会模块是不是有点懵,其实个人认为组件化和模块化只不过是概念不同,模块化包含组件化,组件化是模块化开发中不可缺少的,二者只不过是划分方式不同,实现方面没有太大区别。
2.2 组件或模块的单独运行
组件或模块的单独运行时组件化的又一亮点。还是以我们的demo为例,如果我们想单独运行module_home模块改怎么办?
首先你要知道一点组件变为一个能独立运行的app要变动那几个地方?我个人认为一般有三个地方需要变动:
- 组件或模块的build.gradle 中的apply plugin: 'com.android.library' 变 apply plugin: 'com.android.application'
- 组件或模块的build.gradle 中的defaultConfig配置中的applicationId 根据情况动态变动,如果作为组件或模块存在则不需要,反之亦然。
-
功能清单文件的变动,你想如果作为组件或模块存在我们是不需要下面这些东西的:
ok,那我们就依次解决这三个问题:
对于是否要将模块或组件单独运行,我们需要定义变量去控制,还是以demo为例,我们在project的gradle.properties文件中定义如下:
于是第一个问题解决了,我们只需要在模块的build.gradle中顶部添加如下代码:
于是第二个问题也解决了,我们只需要在defaultConfig中增加以下代码即可:
这里在提个醒默认情况下我们的applicationId值是我们的包名一致
于是第三个问题也解决了,我们还是在对应的build.gradle文件中的android中增加以下代码:
注意这个我们是要配置相关路径的,如:src/main/runalone/AndroidManifest.xml
那我们在这个位置就有相关文件夹,大家请看:
里面的内容如下(就是平常的啦):
单独运行的清单文件:
作为组件或模块的清单文件:
既然实现了组件的单独运行那么单独调试也就解决了。
2.3 数据的传递与ui跳转跳转
ui的跳转我们主要借助于阿里的Aroute,数据的传递可以Aroute和EventBus结合使用,效果更佳。
ARoute的介绍请参考我之前的文章Android-ARouter
还是以项目为例:
- 跳转调用其它组件(这里主要是分享组件)
比如我们要从首页模块调用x5组件,那么请看相关代码:
首页相关代码,再点击跳转X5按钮后:
@OnClick(R2.id.homemodule_button)
public void onViewClicked() {
ARouter.getInstance().build(COMPONENT_X5).withString("url","https://www.baidu.com/").navigation();
}
x5分享组件相关代码:
数据的回调可以结合EvnetBus,这里就不详细说了。
- 利用IProvider跨moudle的服务调用,主要用于非Activity,因为我在写demo中发现在Activity中不好使。直接上代码了:
应用场景是我们zhongmodule_zixun模块中的Fragment要调用X5组件中的X5Test类中需要用到的方法:
-
首先在common_lib中定义X5CompService接口继承IProvider接口,如下:
2.x5组件中的X5Test类实现X5CompService接口
3.在zhongmodule_zixun模块中的Fragment中获取X5CompService实例进行调用,进行了简单的toast
注意:既然是zhongmodule_zixun模块中的Fragment中获取X5CompService的实现类进行调用,那么它就要把X5组件作为依赖:
2.4 生命周期管理
生命周期的管理我们主要是通过common_lib中的baseApplication(注:其他module中的Application都要继承baseApplication,确保唯一性)来管理,这里我直接贴出BaseApplication中的所有代码:
public class BaseApplication extends Application {
//是否开启调试
private boolean isDebug =true;
//全局唯一的context
private static BaseApplication application;
//Activity管理器
private ActivityManage activityManage;
@Override
public void onCreate() {
super.onCreate();
application = this;
activityManage = new ActivityManage();
//初始化路由
initRouter();
}
/**
* 程序终止的时候执行
*/
@Override
public void onTerminate() {
super.onTerminate();
exitApp();
}
/**
* 退出应用
*/
public void exitApp() {
activityManage.finishAll();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
/**
* 初始化路由
*/
private void initRouter() {
//必须在初始化之前写入这两行
if (isDebug) {
//打印日志
ARouter.openLog();
//开始调试
ARouter.openDebug();
}
//ARouter的实例化
ARouter.init(this);
}
/**
* 获取全局唯一上下文
*
* @return BaseApplication
*/
public static BaseApplication getApplication() {
return application;
}
/**
* 返回Activity管理器
*/
public ActivityManage getActivityManage() {
if (activityManage == null) {
activityManage = new ActivityManage();
}
return activityManage;
}
}
ActivityManage使我写的Activity管理工具类,详情请看相关代码。
2.5 其他
- 混淆
混淆我们是放在各个Module还是app的proguard-rules.pro文件中,答案是app的proguard-rules.pro文件中,因为如果在组件中进行混淆,一旦代码出现了bug,这个时候就很难根据日志去追踪bug产生的原因,而且不同组件分别进行混淆非常不方便维护和修改。 -
使用ButterKnife遇到的问题
当我将组件单独运行时是没有问题的,可如果作为module时就会出现什么需要常量等问题,解决办法是:
将原本的R.id.homemodule_button改为R2.id.homemodule_button,如果找不到R2,请确保你project的build.gradle中:
但依赖的butterknife版本是8.4.0:
每一个用到的模块中的build.gradle中都需要配置(默认模块都依赖common_lib,否则单独添加8.4.0依赖):
apply plugin: 'com.jakewharton.butterknife' 以及
就这么多吧,详情请看组件化Demo.