学海模块化方案(拆)

为什么要模块化

当一个App用户量增多,业务量增长以后,就会有很多开发工程师参与同一个项目,人员增加了,原先小团队的开发方式已经不合适了。原先的一份代码,现在需要多个人来维护,每个人的代码质量也不相同,在进行代码Review的时候,也是比较困难的,同时也容易会产生代码冲突的问题。同时随着业务的增多,代码变的越来越复杂,每个模块之间的代码耦合变得越来越严重,解耦问题急需解决,同时编译时间也会越来越长。人员增多,每个业务的组件各自实现一套,导致同一个App的UI风格不一样,技术实现也不一样,团队技术无法得到沉淀。

模块化的好处

  • 多团队并行开发测试
  • 模块间解耦、重用
  • 可单独编译打包某一模块,提升开发效率。


第一阶段

接口设计

设计接口分为两层接口。第一层为获取各个模块接口的工厂层,内置DataModule来解决模块之前的数据传递问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public interface ModuleFractory {
/**
* 初始化
* @param context
*/
void init(Context context);

/**
* 获取数据模块
* @return
*/
DataModule getDataModule();

/**
* 获取用户模块
* @return
*/
UserModule getUserModule();
}

第二层为模块内部用来暴露当前模块所要提供给外部模块使用的函数层接口,通过注解Module来找到此接口对应的实现类。

1
2
3
4
5
6
7
8
9
10
11
@Module("com.xxx.UserModuleImpl")
public interface UserModule extends BaseModule {

boolean isLogin();

UserInfo getUserInfo();

//可以通过Router的方式跳转
void startLoginActivity();

}

数据传递

数据传递可以使用模块化自带的DataModule模块来处理,也可以使用EventBus或者广播来处理。推荐使用DataModule。伪代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Module("com.xxx.DataModuleImpl")
public interface DataModule extends BaseModule {

<T> void addDataUpdateListener(OnDataUpdateListener<T> listener);

<T> void removeDataUpdateListener(OnDataUpdateListener<T> listener);

void notifyDataChanged(Object data);

void removeAllDataUpdateListener();


interface OnDataUpdateListener<T> {
void onDataUpdate(T data);
}
}

第二阶段

拆分应用

把当前app拆分。将XHLibrary、第三方库、ModuleFractory放到一个common Library中。

  • common Library不允许@Bind(R.id.tvDialogTitle)这种形式所以需要使用findviewById或者是升级butterknife到8.x的版本,使用R2来代替。
  • R文件com.xh.abrutch.R要被替换成com.xh.common.R
  • 资源文件的移动,包括anim、color、drawable、layout、mipmap、attrs、colors、dimens、strings、styles
  • 解决common Library中引用模块代码的问题。比如V9Migration这个类引用了用户模块中的MineDownloadBookResBeanDao类
  • 解决common Library中spellword包下的引用问题。

确定依赖关系

各个模块依赖于Common Library,App依赖于各个模块。

总结

  1. 编写ModuleFractory模块。
  2. 已package的形式管理代码,达到模块之间没有任何引用。即删除一个包下的所有代码。应用程序可以正常跑起来。
  3. 新建common library,将所有模块公共的代码及资源文件加入,并完成所有模块对此库的依赖。
您的支持是我原创的动力