Android模块化开发实践

重复造轮子是肯定的

区分概念(组件化,模块化,插件化)

1.组件化
封装可重用功能代码,例如网络组件,数据库组件,图片组件,工具组件等,偏向在纵向的封装,一般在最底层,提供依赖。
2.模块化
重点说下他是对业务的分块,例如个人业务,直播业务,订单业务,商城业务等。偏向横向的封装。其实就是把原来按包名区别业务,现在用module形式单独分块。当然这一分,就需要解决很多问题(后续提出)。
3.插件化
让应用可以在运行时插入想要的东西,可以是代码|模块|组件,其实更加强调的是运行时插入这个动作(热更新技术)


模块化模型图.png

如图,其实在开发上组件化和模块化其实相互辅助。

我的项目实践
1.项目UML
项目UML.png
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下统一管理应用的暴露服务


image.png
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的使用分析

你可能感兴趣的:(Android模块化开发实践)