Android模块化与ARouter框架
在APP开发的初期,代码了不大,业务量比较下,一个APP作为一个单独的模块进行开发,往往问题不大,而且还能加快开发效率但是随着APP的用户量越来越多,也越来越复杂,这种开发方式显得结构特别的臃肿,特别是多个开发人员进行开发维护一个项目的时候每个人的代码质量也不相同,容易会产生代码冲突的问题。同时随着业务的增多,代码变的越来越复杂,每个模块之间的代码耦合变得越来越严重,导致代码混乱,没法进行单元测试,bug修改往往会牵一发而动全身,改掉一个bug,有产生新的bug,同时编译时间也会越来越长,这时候解耦问题急需解决。APP模块化的目标是告别结构臃肿,让各个业务变得相对独立,业务模块在组件模式下可以独立开发,而在集成模式下又可以变为依赖包集成到“app壳工程”中,组成一个完整功能的APP。
一、模块化与组件化区别
1、模块化
- 使用:按照项目功能需求划分成不同类型的业务框架(例如:首页,视频,个人中心,支付……)。
- 目的:隔离/封装。
- 依赖:模块之间有依赖的关系,可通过路由器进行模块之间的耦合问题。
- 特点:高内聚、低耦合。
- 架构定位:横向分块(位于架构业务框架层)。
2、组件化
- 使用:各种自定义的控件、工具类等重复应用的代码等等。
- 目的:复用,解耦。
- 依赖:组件之间低依赖,比较独立。
- 特点:高重用、低耦合。
- 架构定位:纵向分层(位于架构底层,被其他层所依赖)。
因此,在模块化开发的同时引入组件化将会使的工程更加的架构更加清晰。
二、组件化的整体架构
- app:项目的宿主模块,仅仅是一个空壳,依赖于其他模块,成为项目架构的入口。
- baselibrary:项目的基类库,每个子模块都依赖共享公用的类和资源,防止公用的功能在不同的模块中有多个实现方式。
- module_begin:闪屏页,引导页,主页等。
- module_home:首页模块。
- module_mine:我的模块。
- module_video:视频模块。
从上面的例子可以看出各个模块的划分是根据业务划分,这样的好处显而易见,当项目业务很庞大时候,在维护项目过程中只需要更改某一模块,而不涉及其他的模块,这样更加解耦,更加安全,另外各个模块可单独编译打包某一模块,提升开发效率。
三、模块的独立运行
在项目开发中,各个模块可以同时开发,独立运行而不必依赖于宿主APP,也就是每个module是一个独立的app,项目发布的时候依赖到宿主APP中。各业务模块之间不允许存在相互依赖关系,但是需要依赖基类库。单一模块生成的apk体积也小,编译时间也快,开发效率会高很多,同时也可以独立测试。要实现这样的效果需要对项目做一些配置。
1、gradle.properties配置
在项目gradle.properties中需要设置一个开关,用来控制module的编译,如下:
2、清单文件配置
module清单文件需要配置两个,一个作为独立项目的清单文件,一个作为库的清单文件,以module_begin模块为例:
buildApp作为依赖库的清单文件,和独立项目的清单文件buildModule区别是依赖库的清单文件Application中没有配置入口的Activity,其他都一样,如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.arouter.module_begin"><applicationandroid:allowBackup="true"android:supportsRtl="true"android:theme="@style/Theme.AppCompat.Light.NoActionBar"><activity android:name="com.arouter.module_begin.MainActivity"/><activity android:name="com.arouter.module_begin.SplashActivity"/></application> </manifest>
3、gradle配置
gradle文件中加入:
if (isModule.toBoolean()) {apply plugin: 'com.android.application' } else {apply plugin: 'com.android.library' }android {defaultConfig {if (isModule.toBoolean()) {applicationId "com.arouter.module_match"}sourceSets {main {if (isModule.toBoolean()) {manifest.srcFile 'src/main/buildModule/AndroidManifest.xml'} else {manifest.srcFile 'src/main/buildApp/AndroidManifest.xml'}}}} }dependencies {implementation project(':baselibrary') }
4、宿主app配置
dependencies {if (!isModule.toBoolean()) {implementation project(':module_home')implementation project(':module_video')implementation project(':module_begin')implementation project(':module_mine')} }
经过这四步配置之后就可以通过isModule进行配置,当isModule为true的时候作为单独的模块进行运行,当isModule为false作为依赖库,从而进行灵活的开发。
四、模块之间的通信之ARouter
模块与模块之间不存在依赖关系,而是各自运作的,页面的跳转就不能直接startActivity调用具体的activity了,因为这个Activity已在另外一个模块,中那么如何进行页面跳转,管理页面?这里要谈到页面路由的概念,简单的来说映射页面跳转关系的,当然它也包含跳转相关的一切功能。而是ARouter开源框架恰好满足这个需求,他是是阿里巴巴开源的Android平台中对页面、服务提供路由功能的中间件,提供跨模块API调用,通过控制反转来做组件解耦,提供参数传递、解析,跳转拦截等强大的功能,目前start数量已经接近7K。
1、集成
(1)在app和baselibrary的gradle中分别引入:
android {defaultConfig {javaCompileOptions {annotationProcessorOptions {arguments = [moduleName: project.getName()]}}} }annotationProcessor 'com.alibaba:arouter-compiler:1.1.4' compile 'com.alibaba:arouter-api:1.3.1'
(2)在所有的module的gradle中加入:
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'android {defaultConfig {javaCompileOptions {annotationProcessorOptions {arguments = [moduleName: project.getName()]}}}}
arouter-api的作用是引入arouter,只需要在app和baselibrary中引入,而arouter-compiler是通过arouter依赖注入生成一些额外的的代码,必须在每个gradle中引入,否则没法生成依赖的代码,导致跳转失败;javaCompileOptions也是每个必须的,否则编译失败。
2、注册
集成之后还需要ARouter进行注册,建议在Application中进行注册,如下:
public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();initRouter(this);}private void initRouter(MyApplication myApplication) {if (BuildConfig.DEBUG) {ARouter.openLog(); // 打印日志ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)}ARouter.init(myApplication);}@Overridepublic void onTerminate() {super.onTerminate();ARouter.getInstance().destroy();}}
别忘记在清单文件中进行设置application,除此之外还需要在使用ARouter的地方再次进行注入,否则跳转不成功:
ARouter.getInstance().inject(this);
一般在基类Activity或者Fragment进行设置,让其他子类继承即可:
public class BaseActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ARouter.getInstance().inject(this);}}
3、模块间页面跳转
(1)最简单的页面跳转
ARouter.getInstance().build("/begin/MainActivity").navigation();@Route("/begin/MainActivity")public class MainActivity extends BaseActivity {}
这样就可以跳转到MainActivity,需要注意的是"/begin/MainActivity"至少需要两级目录,不同模块之间第一个目录应该是不同的,一般以模块名称作为一级目录,Activity名称作为二级目录,Fragment类似。
(2)携带参数的页面跳转
ARouter.getInstance().build("/begin/MainActivity").withString("name", "张三").withInt("age", 30).navigation();@Route(path = "/begin/MainActivity")public class ClickButtonActivity extends BaseActivity {@Autowired(name = "name")public String name;@Autowired(name = "age")public int age;}}
通过@Autowired注解可以获取到传递过来的数据,注解后面的name可以省略掉,如果省略掉会以下面成员变量作为key进行获取,为了保险期间尽量设置name,否则key与成员变量不一致会导致失败,获取的数据为null。
(3)携带监听的页面跳转
ARouter.getInstance().build("/begin/MainActivity").withString("name", "张三").withInt("age", 30).navigation(getActivity(), new NavigationCallback() {@Overridepublic void onFound(Postcard postcard) {Log.e("TAG", "onFound:找到了");}@Overridepublic void onLost(Postcard postcard) {Log.e("TAG", "onLost:没找到");}@Overridepublic void onArrival(Postcard postcard) {Log.e("TAG", "onArrival:完成了");}@Overridepublic void onInterrupt(Postcard postcard) {Log.e("TAG", "onInterrupt:被拦截了");}});@Route(path = "/begin/MainActivity")public class ClickButtonActivity extends BaseActivity {@Autowired(name = "name")public String name;@Autowired(name = "age")public int age;}}
NavigationCallback就是跳转的监听的回调,跳转成功、失败、完成、被拦截四个监听回调,这在个回调里面可以进行埋点设置、其他跳转、验证登录等等。
(4)跳转的事件拦截
ARouter提供了全局跳转的拦截功能,这个拦截功能用于跳转的前期做一些其他事情,例如验证登录等,可以定义多个拦截器,只需要加上@Interceptor注解就可以,不用在其他进行进行调用,注解上可以携带参数,例如@Interceptor(priority = 4),priority表示优先级,数字越小越会优先拦截,此外还有个name属性表示拦截器名称,基本上不怎么使用。
@Interceptor(priority = 4)public class TestInterceptor implements IInterceptor {@Overridepublic void process(Postcard postcard, InterceptorCallback callback) {//进行逻辑判断if (true) {callback.onContinue(postcard);Log.e("TestInterceptor","正常");} else {// callback.onInterrupt(null);callback.onInterrupt(new RuntimeException("异常"));Log.e("TestInterceptor","数据异常进行拦截");}}@Overridepublic void init(Context context) {}}
但是需要注意的是,每次所有的跳转都会执行拦截器操作,arouter提供了greenChannel()方法进行跳转过去一切拦截器,在不需要拦截器的地方跳转的时候加上即可。
ARouter.getInstance().build(JumpUtil.MainActivity).greenChannel().navigation();
4、模块间服务调用
用于模块之间进行服务的调用,可以获取其他模块的方法和数据。
(1)在公共模块baselibrary中注册接口一个接口继承自IProvider,加上所需要的方法。
public interface IUserInfo extends IProvider {String getName();}
(2)在提供服务的模块中实现这个接口,并且加上@Route注解,设置路径。
@Route(path = "/service/UserService")public class UserInfoImpl implements IUserInfo {@Overridepublic String getName() {return "张三";}@Overridepublic void init(Context context) {}}
(3)在需要获取服务的地方进行数据获取
String name = ARouter.getInstance().navigation(IUserInfo.class).getName();
这样就可以调用其他模块的方法,获取到其他模块服务提供的数据了。
五、模块间的数据传递
(1)广播
在一个模块中发送广播设置数据,在另一个模块中注册广播接收数据,使用广播进行数据传递方式广播相对于其他的方式而言消耗资源较多。
(2)EventBus
调度灵活,不依赖于 Context,快速且轻量,在这里不做介绍。
六、模块化的注意事项
1、编译失败
(1)没有在每个build.gradle中加入:
javaCompileOptions {annotationProcessorOptions {arguments = [moduleName: project.getName()]} }
(2)依赖资源冲突
改为所以依赖的包版本一致,统一起来。
(3)没有注册
在Application下进行注册ARouter。
2、跳转失败
(1)在基类或者onCreate中没有注入。
ARouter.getInstance().inject(this);
(2)分组错误
不同模块的moudle在ARouter是不同的组,如果没有设置分组,默认使用一级目录分组,在设置一级目录的时候不同模块需要设置不同名称,一般以module名称命名一级目录名称。
(3)没有添加arouter-compiler依赖
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
到此结束!!!
demo运行效果如下:
最后附上源码地址:https://download.csdn.net/download/yoonerloop/10540781点击打开链接
Android模块化与ARouter框架相关推荐
- Android项目解耦--路由框架ARouter的使用
Android项目解耦–路由框架ARouter源码解析 前言 随着业务量的增长,客户端必然随之越来越业务和功能模块耦合越来越生,开发人员代码维护成本越来越高. App一般都会走向组件化.插件化的道路, ...
- Android项目解耦--路由框架ARouter源码解析
前言 上一篇文章Android项目解耦–路由框架ARouter的使用讲述了ARouter在项目中的使用,这边文章主要对ARouter的源码进行学习和分析. ARouter的结构 ARouter主要由三 ...
- Android平台页面路由框架ARouter原理
本次分享将主要围绕以下几个方面: 一.为什么需要路由框架 二.ARouter的技术方案 三.使用ARouter的最佳实践 四.未来开发计划 一.为什么需要路由框架 原生的路由方案存在的问题 首先谈一谈 ...
- 美团猫眼android模块化实战-可能是最详细的模块化实战
转载请注明出处: 美团猫眼电影android模块化实战–可能是最详细的模块化实战 地址:http://blog.csdn.net/qq_22744433/article/details/7794844 ...
- 美团猫眼电影android模块化实战--可能是最详细的模块化实战
转载请注明出处: 美团猫眼电影android模块化实战–可能是最详细的模块化实战 地址:http://www.jianshu.com/p/d372cc6802e5 目录 1 写这篇博客的初衷 首先一句 ...
- 浅谈Android模块化设计(常规思路)
移动开发从iPhone手机问世之后,也快有十个年头了,随着App功能的不断的变多,代码规模越来越大,也为了适应多项目组协同开发的工程需要,各种移动端的模块化方案应运而生. 这两年来,各大公司以及各路大 ...
- 个人自用总结的Android模块化架构模板
模板项目链接 ArchitectureSample 如果大家觉得有什么问题或者建议,欢迎提issue,这个工程我也会不断改进,虽然比不上大公司.大牛的那些NB架构,但自己不断学习改进也是一种进步吧. ...
- Android专题-常用第三方框架
Android专题-常用第三方框架 HTTP网络请求 带*号的是个人推荐比较好用的 HTTP网络请求 okhttp * :https://github.com/square/okhttp retrof ...
- Android 2018优秀开源框架整理收藏
中级.高级.资深工程师 知其然知其不可然 <框架百大排行榜>里所提到的流行词.流行术语--使用能力.融会贯通其原理.讲解框架能力的高低,将让你不断的在这三个级别徘徊: 会有意识的合并榜单里 ...
最新文章
- PostgreSql、MySql字段值为空时取其他值语句
- 在Anacoda中管理多个版本Python
- 举头望明月打计算机术语,精选有关月亮的灯谜大全
- 如何访问MATLAB Builder for .NET Components
- idea alt+insert快捷键中implement
- 深度学习在自然语言处理的应用
- JVM-12虚拟机性能监控与故障处理工具之【JDK的可视化工具-VisualVM】
- linux系统无法用命令行,无法在Linux操作系统上从命令行启动Kitchen Pentaho作业
- 《剑指offer》-- 和为S的连续整数序列、和为S的两个数字、左旋转字符串、翻转单词顺序列
- 商品评价 - 实现分页
- ansible结合playbook批量部署war包项目上线
- popwindow setFocusable(false) 不消失与弹出软键盘的冰火不容的矛盾
- js异步请求php数据,原生JS发送异步数据请求实例详解
- Docker 容器使用
- PowerDesigner通过jdbc连接MySQL实现逆向工程步骤
- 2019最新某私塾在线高级java软件架构师实战培训教程
- 代理软件使用拨号不可用,使用wifi正常使用 解决 win10
- 高数 | 【微分方程】技巧性例题 及 李林880详解
- 世界著名半导体公司及其官网
- 解决电脑端微信浏览器不支持vue、axios等问题