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<String, RouterInfo> 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<String> paramNames = new ArrayList<>();
        for (Map.Entry<String, Object> entry : routerInfo.getParams().entrySet()) {
            if (entry.getValue() != null) {//本地固定
                setParams(build, entry);
            } else {
                paramNames.add(entry.getKey());
            }
        }
        String[] paramHref = getParameter(path);

        ArrayMap<String, Object> arrayMap = getParamMap(paramNames, paramHref);

        for (Map.Entry<String, Object> 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的使用分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容