剖析Android项目组件化重构架构

1.组件化重构效果这里先看下我们重构前后的框架图比较:
重构前:
传统代码架构.png
重构后
组件化代码架构.png
ft_xxx表示业务层模块 lib_xxx表示基础库模块重构后的架构图如下 :
服务接口调用.png
重构前的代码业务封装在宿主app中,业务耦合严重,如果修改一个业务模块,需要对整个app进行完整测试,测试工作量巨大
而重构后,我们只需要对单一app进行独立调试即可。
重构后的框架结构:所有的业务组件之间通讯都通过ft_base_service进行通讯
2.组件化重构准则1.单一业务可以单独调试,也可以作为lib提供给宿主app使用2.同一级别的模块不允许直接调用,比如我们的ft_home组件不允许直接调用ft_login组件,不然组件化的意义就不存在了3.组件间通讯不能直接使用显示的class文件跳转,可以考虑很用arouter框架进行解耦4.每个组件可打包为aar或者jar上传到maven私服,宿主使用的时候,直接引用私服中aar包即可能做到以上几点,你的app就可以称为一个组件化框架的app了。
3.组件化重构思路
重构思路.png
拆 :拆代码,拆资源,拆构建
由于所有业务和资源都耦合在宿主app中,所以需要将代码和资源拆开到对应模块中
当然我们的构建build.gradle也需要拆分到不同模块中
接 :对外提供接口
组件化之间不能直接通讯,需要使用暴露接口的方式对外通讯
测 :反复测试
重构后代码,需要反复测试,防止出现意想不到的bug
4.组件化重构过程这里我以登录业务ft_login为例子:
1. 步骤1 :首先新建一个业务模块ft_login,然后在宿主app中将登录功能相关联的代码和资源抽离到ft_login中2. 步骤2 :将和登录构建相关的依赖分配到ft_login构建中。3. 步骤3 :单独调试功能实现3.1:在gradle.properties中创建一个全局变量:isrunalone=true3.2:在build.gradle中:if(isrunalone.toboolean()){ apply plugin:'com.android.application'}else{ apply plugin:'com.android.library'}android { compilesdkversion 33 buildtoolsversion 33.0.0 defaultconfig { if(isrunalone.toboolean()){ applicationid 'com.anna.ft_login' } ... } sourcesets { main { java { srcdirs = ['src/main/java'] } resources { srcdirs = ['src/main/res'] } aidl { srcdirs = ['src/main/aidl'] } manifest { if(isrunalone.toboolean()){ srcfile 'src/main/manifest/androidmanifest.xml' }else { srcfile 'src/main/androidmanifest.xml' } } } }}def dependlist = [rootproject.depslibs.okhttp, rootproject.depslibs.gson, rootproject.depslibs.appcompact, rootproject.depslibs.design, rootproject.depslibs.eventbus, rootproject.depslibs.arouterapi, ':lib_network',':lib_common_ui',':ft_base_service']dependencies { if(!isrunalone.toboolean()){ dependlist.each { string depend -> depend.startswithany(':lib',':ft')? compileonly(project(depend)):compileonly(depend){ switch (depend){ case rootproject.depslibs.arouterapi: exclude group: 'com.android.support' break; } } } }else { dependlist.each { string depend -> depend.startswithany(':lib',':ft')? implementation(project(depend)):implementation(depend) { switch (depend) { case rootproject.depslibs.arouterapi: exclude group: 'com.android.support' break; } } } } //arouter注解处理器 annotationprocessor rootproject.depslibs.aroutercompiler testimplementation 'junit:junit:4.+' androidtestimplementation 'androidx.test.ext:junit:1.1.3' androidtestimplementation 'androidx.test.espresso:espresso-core:3.4.0'}单独调试状态下注意四点 :
1.引用application插件2.引入applicationid3.引入不同给的sourcesets构建路径4.引入的库单独调试状态下需要使用implementation导入,不能使用compileonly实现上面四点, 只要打开isrunalone就可作为一个单独app运行了 。
4.步骤4:组件间通讯这里,我们引入一个ft_base_service模块,这个模块用来实现组件间通讯用,需要调用别的业务模块都需要使用这个模块才能通讯、
业务模块与ft_base_service之间通讯使用的是路由arouter:
关于arouter的使用可以参考这篇文章:
android开源系列-组件化框架arouter-(一)使用方式详解
1.创建ft_base_service,在这个模块中:创建一个loginservice接口继承iprovider引入arouter依赖:
android { javacompileoptions { annotationprocessoroptions { arguments = [arouter_module_name: project.getname(), arouter_generate_doc: enable] } }}//arouter核心apiimplementation rootproject.depslibs.arouterapi//arouter注解处理器annotationprocessor rootproject.depslibs.aroutercompiler创建loginservice:
public interface loginservice extends iprovider { boolean haslogin(); void login(context context);}2.在ft_login业务模块中实现loginservice接口注意这里因为使用了arouter注解,所以也需要引入arouter依赖
@route(path = /login/login_service)public class loginserviceimpl implements loginservice { context context; @override public boolean haslogin() { return usermanager.getinstance().haslogined(); } @override public void login(context context) { loginactivity.start(context); } @override public void init(context context) { log.d(tag,loginserviceimpl is init); }}3.在ft_base_service模块中对loginservice接口进行依赖注入public class loginimpl { @autowired(name = /login/login_service) public loginservice mloginservice; private static loginimpl mloginimpl = null; public static loginimpl getinstance() { if (mloginimpl == null) { synchronized (loginimpl.class) { if (mloginimpl == null) { mloginimpl = new loginimpl(); } return mloginimpl; } } return mloginimpl; } private loginimpl(){ arouter.getinstance().inject(this); } public boolean haslogin(){ return mloginservice.haslogin(); } public void login(context context){ mloginservice.login(context); }}笔者使用了一个 单例类loginimpl ,在构造器中对loginservice依赖注入
arouter.getinstance().inject(this);
然后宿主app或者其他模块引用登录业务功能时,需要依赖ft_base_service模块,并使用loginimpl的接口即可。
这里要说明下,平时我们使用的四大组件跳转也可以使用这个方式来处理,在服务接口中定义跳转接口即可。当然也可以使用arouter的activity跳转方式或者fragment实例获取方式
5.代码打包aar上传到maven私服:关于这块maven私服更多内容可以参考这篇文章:
gradle筑基篇(六)-使用maven实现组件化类库发布
这里我们封装了一个通用组件发布库:
apply plugin: 'maven'uploadarchives { repositories { mavendeployer { // 是否快照版本 def issnapshot = boolean.valueof(maven_is_snapshot) def versionname = maven_version if (issnapshot) { versionname += -snapshot } // 组件信息 pom.groupid = maven_group_id pom.artifactid = maven_artifactid pom.version = versionname // 快照仓库路径 snapshotrepository(url: uri(maven_snapshot_url)) { authentication(username: maven_username, password: maven_username) } // 发布仓库路径 repository(url: uri(maven_release_url)) { authentication(username: maven_username, password: maven_username) } println(################################### + \\nuploadarchives = + pom.groupid + : + pom.artifactid + : + pom.version + . + pom.packaging + \\nrepository = + (issnapshot ? maven_snapshot_url : maven_release_url) + \\n################################### ) } }}然后在对应的组件下面引用:
apply from:file('../maven.gradle')发布的时候直接在gradle面板中点击uploadarchives任务即可
task面板.png
经过上面几个步骤就基本完成了login组件的封装并发布,且对外提供了login组件接口 其他组件也是按照上面的逻辑进行重构
更多详细信息可以自己拿到项目源代码查看。
5.组件化重构总结组件化不仅是一种架构,更是一种思想,架构是可以变得,但是核心思想却是统一的,在拆分代码的时候,要注意模块的颗粒度,不是颗粒度越小就越好,模块分离的好,后期对组件改造会有很大帮助, 关于组件化的文章就讲到这里,组件化重构的项目已经上传到github。后面会出一期插件化的项目改造。敬请期待。
** demo地址: https://github.com/byteyuhb/anna_music_app **

用iPad充电器给iPhone充电,将会产生怎样的后果
区块链电子票据你见过吗
将食品加热器改造为一个紫外线驱动的移动净化装置
魅族Flyme6系统今天正式推送,颠覆式全新设计秒杀苹果
LED芯片漏电烧电极掉电极案例分析
剖析Android项目组件化重构架构
对现代EDA技术的概念及特点介绍
你与明星软件开发工程师只差这10个特质
EMC设计和EMC问题改进的方法解析
OPPO ColorOS 7正式亮相 采用全新轻视觉设计理念
火热背后,中国物联网发展道路惊现两大“拦路石”
特斯拉自动驾驶“撞死”机器人 机器人受损严重且无法修护
LX9 Microboard之初试手试用初体验(1)
做好访客登记,把好企业内部环境安全第一关
抢占式内核
平安城市建设成为城市治安防控的组成部分 也是实现智慧城市的重要因素
1LCD投影首超DLP,康佳投影仪严控品质打破行业困境
为什么在使用过程中热像仪测温会出现不准的情况?
利用英飞凌IGBT单管设计手提式焊机
思必驰科募资10.33亿元进行AI核心技术创新升级