重复造轮子是肯定的
区分概念(组件化,模块化,插件化)
1.组件化
封装可重用功能代码,例如网络组件,数据库组件,图片组件,工具组件等,偏向在纵向的封装,一般在最底层,提供依赖。
2.模块化
重点说下他是对业务的分块,例如个人业务,直播业务,订单业务,商城业务等。偏向横向的封装。其实就是把原来按包名区别业务,现在用module形式单独分块。当然这一分,就需要解决很多问题(后续提出)。
3.插件化
让应用可以在运行时插入想要的东西,可以是代码|模块|组件,其实更加强调的是运行时插入这个动作(热更新技术)
如图,其实在开发上组件化和模块化其实相互辅助。
我的项目实践
1.项目UML
2 要解决的问题
2.1butterknife 在module的R文件问题
原代码迁移到module里使用Butterknife要主意R2问题,这是个苦力活。
2.2建立style-module
建立统一的color,dimens,通用的图标文件,当然也可以放在base里,当单独分开更利于日后新项目的迁移。整个模块代码没有太大问题,需要和UI团队配合,建立起统一UI标准。
3.3路由-ARouter
跨模块的跳转,这是模块化最易遇到的问题,使用ARouter强大功能可以解决这个问题。
/**
* 路由列表
*/
public class RouterPath implements ILivePath, IUserPath {
}
/**
* 直播模块
*/
interface ILivePath {
String ROUTER_LIVE = "/live/LiveActivity";
String ROUTER_CREATE = "/live/CreateLiveActivity";
String ROUTER_APPLY_HOST = "/live/ApplyHostActivity";
}
/**
* 用户模块
*/
interface IUserPath {
String ROUTER_ACCOUNT = "/user/accountActivity";
String ROUTER_MY = "/user/userFragment";
String ROUTER_LEVEL = "/user/levelActivity";
String ROUTER_EARNING = "/user/earningActivity";
}
当然也会后台运营要求所以定义了BaseRouter工具来作为运营跳转。
public class BaseRouter {
private static final HashMap ROUTER_MAP = new HashMap<>();
static {
params = new ArrayMap<>();
params.put("hostId", null);
params.put("isHost", false);
ROUTER_MAP.put("fm://anchor", new RouterInfo(RouterPath.ROUTER_LIVE, params));
}
public static void startRouter(String path, RouterInfo routerInfo) {
Postcard build = ARouter.getInstance().build(routerInfo.getRouterUrl());
List paramNames = new ArrayList<>();
for (Map.Entry entry : routerInfo.getParams().entrySet()) {
if (entry.getValue() != null) {//本地固定
setParams(build, entry);
} else {
paramNames.add(entry.getKey());
}
}
String[] paramHref = getParameter(path);
ArrayMap arrayMap = getParamMap(paramNames, paramHref);
for (Map.Entry entry : arrayMap.entrySet()) {
setParams(build, entry);
}
build.navigation();
}
....
}
而原先简单界面跳转也统一使用ARouter。
@Route(path = RouterPath.ROUTER_LIVE)
public class LiveRoomActivity extends BaseActivity {
public static void launch(Context mContext, String hostId, String pic) {
// Intent intent = new Intent(mContext, LiveActivity.class);
// intent.putExtra("hostId", hostId);
// intent.putExtra("isHost", false);
// intent.putExtra("pic", pic);
// mContext.startActivity(intent);
ARouter.getInstance().build(RouterPath.ROUTER_LIVE).withString("hostId",hostId).withString("pic",pic).withBoolean("isHost",false).navigation();
}
...
}
是否建立统一的跨模块跳转聚合类,那就看个人了。
4.数据共享问题
业务场景:整个应用的用户数据保存在User模块下,如Live模块需要调用User模块的用户信息。这个时候就需要User模块提供用户信息服务暴露出来。
使用ARouter的暴露服务。当然也可以用一个全局的数据库来保存,但是这样就需要把User模块的实体类下沉到Base基础模块下,这样似乎有无法真正的模块化分开。(有好的建议希望可以提提)
在base下统一管理应用的暴露服务
public interface IUserMangerService extends IProvider {
String getUserCover();
String getUserToken();
String getUserName();
String getUserHead();
String getUserId();
void saveCover(String url);
}
在User模块实现IUserMangerService
@Route(path = "/user/userService", name = "UserService")
public class UserManagerService implements IUserMangerService {
@Override
public String getUserCover() {
return UserManager.getInstance().getUserInfo().getAnchor_cover();
}
@Override
public String getUserToken() {
return UserManager.getInstance().getToken();
}
@Override
public String getUserName() {
return UserManager.getInstance().getUserInfo().getNickname();
}
@Override
public String getUserHead() {
return UserManager.getInstance().getUserInfo().getHead();
}
@Override
public String getUserId() {
return UserManager.getInstance().getUserInfo().getF_uuid();
}
@Override
public void saveCover(String url) {
UserInfoDetailEntity userInfo = UserManager.getInstance().getUserInfo();
userInfo.setAnchor_cover(url);
UserManager.getInstance().saveUserInfo(userInfo);
}
@Override
public void init(Context context) {
}
}
在Live模块下创建这个模块下的服务管理
public class LiveServiceManager {
private static LiveServiceManager mInstance;
IUserMangerService iUserMangerService;
public static LiveServiceManager getInstance(Context context) {
if (mInstance == null) {
synchronized (LiveServiceManager.class) {
if (mInstance == null) {
mInstance = new LiveServiceManager( context);
}
}
}
return mInstance;
}
private LiveServiceManager(Context context) {
ARouter.getInstance().inject(context);
this.iUserMangerService = ARouter.getInstance().navigation(IUserMangerService.class);
}
public IUserMangerService getUserServiceManager(){
return iUserMangerService;
};
}
String token = LiveServiceManager.getInstance(getContext().getApplicationContext()).getUserServiceManager().getUserToken();
以上四个问题是需要在项目迁移实践遇到的,当然遇到的问题不止这些,整体项目的实现模块化,需要基础组件一个个耦合,封装好来。还有不同团队间的配合,这也是个大问题。模块化重构需要渐进式的展开,不可一触而就,一个模块一个模块来。
未完。。。
(如Dialog的跨模块化调用,Arouter做不到,是否可以用DialogFramgent来替代呢。还是有其他方法呢)
欢迎大家对以上内容讨论,交流。
美团猫眼android模块化实战
微信Android模块化架构重构实践
ServiceLoader服务提供者模式
组件化构想以及ARouter的使用分析