专栏名称: 开发者全社区
分享和推送Java/Android方向的技术和文章,让你成为这方面的大牛,让你每天都成长一点。同时,我们也会邀请BAT的大牛分享原创!
目录
相关文章推荐
郭霖  ·  这一次,让EventBus纯粹一些 ·  4 天前  
郭霖  ·  HarmonyOS ... ·  5 天前  
鸿洋  ·  大图检测插件的落地 ·  4 天前  
鸿洋  ·  初识Android内存优化 ·  5 天前  
鸿洋  ·  Android AppOpsService是什么? ·  6 天前  
51好读  ›  专栏  ›  开发者全社区

Android架构之App 模块化实战经验总

开发者全社区  · 公众号  · android  · 2017-04-30 08:47

正文

相关阅读:

吊炸天!74款APP完整源码!

Android中多线程的使用四种方式最全总结

那些年收藏的很实用的Android开源库集合(工具)

原文:https://youzanmobile.github.io/2017/04/14/youzan-app-modularization/

为了应对团队壮大之后的开发模式,我们必须要对业务进行隔离,同时沉淀出通用组件,完善移动开发的基础设施。 —— 由彷梓分享


随着有赞电商业务的不断发展壮大,App 端所承担的功能也越来越重,特别是代码几易其主之后开始变得杂乱无章,牵一发而动全局的事情时常发生。为了应对团队壮大之后的开发模式,我们必须要对业务进行隔离,同时沉淀出通用组件,完善移动开发的基础设施。

1. 痛点

模块化之前,我们主要面临以下痛点:

  • 业务边界不清晰

  • 通用代码与业务代码耦合

  • 代码、资源文件大量重复

  • 常量满天飞

其中业务边界不清晰是最大的痛点,最直接的表现就是处处有雷,经常会引入新的 Bug,而且很多 Bug 往往不能从根本上解决,代码维护成本居高不下。

2. 重构原则

模块化并不能一蹴而就,我们在重构的同时也在做新需求,每次看到那一坨旧代码心中就会有无数只”草泥马”奔腾而过,干脆重写的无奈之情难以抑制,结果在红牛的日夜陪伴下写出来的新代码虽然看上去“漂亮”,但是实际上问题更多,得不偿失。吃过几次苦头之后,我们总结出了重构的三项基本原则:

2.1 渐进式重构

如果一段代码已经比较稳定,可以从中抽取一部分功能重写,不要一上来就全部推翻重写,可以慢慢淘汰掉老代码。

2.2 iOS / Android 互相参考

业务代码总是惊人的相似,两端互相参考的过程中,不但可以 Review 代码,还能加深对业务的理解,可谓一举两得。

实践证明,如果人手紧张,项目早期可以只让一端的开发人员跟需求,另一端直接“翻译代码”,甚至一个人写两端代码。

2.3 理清业务再动手

3.1 抽取 Common

Common 层服务于所有的上层业务,是通用层,不允许引用业务层代码。

  1. 首先把 Common 层用到的 Business 层代码下放到各个业务

  2. 然后把多个 Business 之间共用的代码提取到 Common 层

  3. 资源文件的处理方式与代码一致

Common 层作为权宜之计,它的命运是向死而生,最终会诞生出许多功能独立的基础模块。而这个过程是漫长的,我们只能在业务隔离的同时,不断丰富 Common 模块,然后在某个节点将其再拆分成一个一个独立模块。

代码也逃不出分久必合、合久必分的的宿命。

3.2 业务隔离

业务模块之间不能互相依赖,只能单向依赖 common。

业务之间存在两种耦合关系:

  • 页面耦合

  • 功能耦合

要做到彻底隔离就必须打破这两种耦合关系:

  • 页面解耦 - 跳转协议

  • 功能解耦 - 模块间 RPC

3.2.1 统一跳转协议

页面解耦可以借鉴 Web 的设计原理,给业务模块中对外的页面定义一个 URI,然后页面之间通过 URI 跳转。

举个栗子,A、B 两个页面分属于不同的业务模块,在页面未解耦之前,A 如果要跳转到 B,必须要依赖 B 的模块,那么跳转代码会写成如下形式:

Android

Intent intent = new Intent(getContext(), BbbActivity.class);
intent.putParcelable(BbbActivity.EXTRA_MESSAGE, message);
startActivity(intent);


iOS

BbbViewController *bbbVC = [[BbbViewController alloc] init];
bbbVC.messageModel = messageModel;
[self.navigationController pushViewController:bbbVC animated:YES];


如果 A、B 之间还需要传递数据,就要共享常量、Model,耦合继续加重。

如果我们为 B 页面定义一个 URI - wsc://home/bbb,然后把共享的 messageModel拍平序列化成 Json 串,那么 A 只需要拼装一个符合 B 页面 scheme 的跳转协议就可以了。

wsc://home/bbb?message={ "name":"John", "age":31, "city":"New York" }

URL Router 有很多种实现方式,网上资料也是多如牛毛,这里只提供一种思路。

Android 实现方式

  1. 在 AndroidManifest.xml 文件中定义 URI

  2. 封装跳转 Intent


  3. 步骤 2 代码进一步封装


iOS实现方式

  1. 通过 plist 文件保存 URI 到 Controller class 的映射

  2. 封装一个根据 URI 跳转到 Controller 的 SDK

  3. 页面跳转

    [ZanURLRouter routeURL:@"wsc://home/bbb"];

注意事项

  • 两端协议要保持一致

  • 需要通过工程手段保证页面 URI 唯一

3.2.2 模块间 RPC

4 代码管理

如果被隔离的业务模块仍然在一个 Project 中,就无法从“物理”上彻底隔绝代码间的相互引用,我们需要从工程上保证业务之间互相独立。

4.1 代码结构

Android (Module)iOS (Project)

4.2 独立发版

每一个 subproject 可以独立发版,然后通过坐标依赖组装成 App,以 Android 为例:

4.3 独立 Repo


看完本文有收获?请分享给更多人


Java和Android架构

欢迎关注我们,一起讨论技术,扫描和长按下方的二维码可快速关注我们。搜索微信公众号:JANiubility。

公众号:JANiubility