天天看点

pyqt 获取 UI 中组件_网易友品 Android 客户端组件化演进

pyqt 获取 UI 中组件_网易友品 Android 客户端组件化演进
原文作者:简书 - 四单老师

项目背景

主站业务经历了长期的迭代维护,业务的增长同时带来每个版本业务量繁重,迭代周期很快。同时团队也在不断的扩张,对应拆分了组内不同的业务线对接不同业务线的需求,最初的Android客户端单一的设计架构已经逐渐不满足快速的业务开发需求。历经组内讨论开始对项目整理进行组件化的迁移,通过组件化的方式满足不同业务线业务开发的稳定性,是迭代开发更灵活,组内协作开发效率得到提升。同时又有新的项目立项需要投入开发,一方面可以通过新项目实践和推进组件化的迁移,另一方面也可以通过组件化拆分后的技术组件复用来更快的搭建和开发新的项目。

组件化的准备

技术准备

1. 主站最初的app项目只有一个模块,业务耦合严重,技术组件很难复用,所以我们采取的第一步是拆分部分基础组件下沉为一个Base库,尽量去解耦业务提取基础技术组件达到多业务模块的复用,也是为了支持新项目和主站项目多个app的技术支持。

2. 考虑组件化后的业务相对隔离,但是客户端组件间需要建立访问,所以需要组件间通信的介入。我们采取的方式是路由、服务和全局通知。

3. 搭建路由库支持,目的是解决业务组件物理隔离后的UI跳转和访问,通过维护路由表的方式寻址到需要访问的业务组件UI。我们采取的是技术实现是通过注解给对应的业务UI比如LoginActivity上用注解申明对应的路由地址,在公共依赖的接口处公开维护这个路由地址常量,暴露给其他业务组件通过方位该地址来跳转到对应的业务组件UI。

@Router
           

对应Act绑定上路由地址后,需要对路由的地址进行统一的收集管理。同时也为了支持某些服务动态下发的地址,策略是优先在本地的路由表进行匹配,如果查询到了该地址有对应的Native界面优先跳转到Native的界面,未匹配到则跳转到由webView容器承载的网页。目前我们采取的方式是通过APT自动生成对应路由注解后的activity的收集类。

//自动生成的类,命名规则是RouterGenerator+业务组件模块名称
           

然后再通过ASM的方式在编译期对所有加载到工程里面的模块组件通过特定的规则进行上面路由辅助类的收集。

//收集路由地址
           

具体实现不再此展开了,此方式的好处就是可以根据需求加载需要的业务组件并且实现自动注册和收集路由到路由表。如果觉得独立开发路由库的成本较高,也可以采取业界主流的一些路由库比如ARouter等,基本类似。

4. 关于组件间服务通信的方式,目前采取的是暴露对应的服务接口供各个业务组件方调用。每个业务组件都会申明需要对外暴露提供的方法,并在自己的业务组件模块内实现这些具体被调用的方法。对外接口库根据模块划分,可以申明和维护通信间的一些数据类型,比如公开的数据model和对应需要访问的一些路由地址等。为了便于服务的动态收集,这些服务接口可以统一的继承某个规则接口,然后采取上述路由的方式,对所有实现了该规则接口的服务接口统一的收集管理。

facade
           

剩下一些特点场景的业务,比如:登录成功后需要全局通知刷新多个UI某个业务状态的时候,目前采取EventBus的方式进行订阅通知。

5. 在组件base库一定下沉和组件间通信方式的确立,开始对组件的具体的拆分粒度进行划分。大致划分为业务组件和技术组件两部分。

组件化的拆分流程

拆分前的考虑

考虑新的项目投入的人力资源有限,并且需要快速的开发上线,同时业务也有重合的场景。所以当时采取的开发策略是将主站未组件化的代码完全拷贝一份到新项目,并在此的基础上进行改造。改造的原则必须遵循2个应用共建同一套BaseLib,但是由于主站的BaseLib里面会耦合一些自身的业务组件,同时避免对BaseLib的修改影响到主站的业务开发而增加不必要的工作量。当时采取的策略是通过增加一层业务基础组件库来做新项目组件化拆分的缓冲层BaseCompatLib。

拆分过程

pyqt 获取 UI 中组件_网易友品 Android 客户端组件化演进

拆分过程中有很多业务组件共用的情况,结合当时的开发周期可以适当的去解耦部分业务组件重新划分到对应拆分后的业务模块中。如果时间有限,可以先挪到BaseCompatLib这个缓冲成暂时共用待后续再拆,从而避免对2个项目共用的Base库频繁修改带来的负担。

初期的业务模块独立编译的配置方式,仅供参考:

//gradle.properties中申明编译配置是否是独立编译
           

遇到的问题

拆分后的独立模块由于一些基础服务的初始化仍停留在app壳工程,一些sdk或者初始化服务没有统一的管理。优先级混乱并且耦合大量的业务逻辑,导致业务模块拆分后无法独立运行,缺失对应组件所需服务的初始化步骤。开始改造初始化的业务,原理同自动收集一致。

pyqt 获取 UI 中组件_网易友品 Android 客户端组件化演进
interface 
           

彻底组件化

pyqt 获取 UI 中组件_网易友品 Android 客户端组件化演进

组件库的独立发布和维护

原有拆分的本地组件彻底分离出去,采取独立发布和维护的方式迭代更新。

  1. 新建git仓库和本地组件项目,然后以module的方式将原有项目中的业务module导入到本地新建的项目中。推送该项目到git的独立仓库。(目前未采取git subModule的 方式管理,但大致差不多)
  2. 新建的本地项目中再新建一个对应的接口工程用于对外暴露模块中的业务访问。
project: 
           
  1. 添加打包aar发布到maven仓库的脚本用来独立发布login和login-facade模块。
  2. 遵循对应的发布规范,不同项目的app壳工程根据自身的业务需求进行对应的组件依赖,版本开发阶段可采取snapshot的进行依赖。不同的业务组件也可以通过依赖其他不同的业务组件接口达到访问的目的。(如需实际运行,不光需要再接入接口库还需要依赖对应的组件工程)
  3. 目前友品采取的是jenkins的打包发布方式,仅供参考。

本地开发调试模式

在组件开发过程中,单纯的依靠远程方式依赖,对开发阶段的频繁修改不友好。所以我们采取依赖覆盖的方式,让原有的依赖在编译过程中替换掉远程的版本改用本地的版本进行引用。

// 自定义const.gradle环境声明
           

通过以上的方式让Base的依赖从远程替换为本地module的形式。开发阶段就可以通过AS的refactor进行代码的优化和重构,对本地Base修改后到Base的git分支进行对应的提交或MR合回主分支然后走规范的发布打包流程。

组件版本依赖管理

组件项目中会有对Base或者接口库的引用,对于Base我们可以选择compileOnly的方式,也可以选择直接依赖的方式。在集成到项目中后依赖会遵循gradle的依赖传递原则。特别注意:

  1. 避免环形依赖的产生。比如:facade -> base, base -> facade。遇到这种情况需要拆分所需依赖到另外一层。
  2. 在远程依赖替换为本地依赖做开发修改时可能会遇到远程依赖和本地依赖的冲突。比如:app -> login -> com.xxx:base; app -> home -> /localpath/base。 此时可以采取下面的方式进行依赖优先选择本地的方式排除掉其他组件中的远程依赖。
//setting.gradle
           

后续

到此为止基本上组件化就可以持续稳定的开发和维护了,组件化后也给团队的开发效率带来一定的提升,代码也可以在一定可控的范围内稳定的维护。并且在各自维护的组件中,大家也可以根据各自需求选择合适自己业务的开发框架比如:mvp、LiveData、Rx等或者尝试使用新语言Kotlin去编写。解决业务耦合带来的负担同时也使各个组件达到了较高的可复用性,灵活的支持不同的应用项目,达到可插拔的方式集成开发。后续项目也会做一些优化,针对版本依赖的管理和简化组件编译和发布集成的流程来提高协作开发的效率。

ASM自动收集参考:https://github.com/luckybilly/AutoRegister