组件化实践详解(一)

一、目标

本文主要记录组件化项目的实践过程及其中的思考。微信

具体实施一项技术项目以前咱们会首先肯定对应的目标,以后的行动计划都会朝着目标一步步靠拢。网络

  • 大的层面上说就是作到各个模块可以在开发阶段独立运行,充分解耦;
  • 小的层面上说使各个组件(技术、业务组件)更加容易复用;

简单的总结就是把一个大的Project工程,变成若干个小的Module工程,就是这样。框架

看到这个图你们是否是想你要说的原来这么简单啊,先把刀都放下好好说话,实际上组件化看起来简单可是实践道路确是异常艰难,不信咱们继续看!工具

二、基础库抽取

最初咱们的Project总体只有一个Module,也就是说业务代码和技术代码都在一块儿,经过package的方式区分开来的。这种状况下作组件化难度是极大的,注意不夸张的说就是极大,由于你们在一块儿牢牢的抱成一团,相互依赖,然而要想直接拆出来困难重重:首先要把全部的基础库抽取出来到单独的基础Library工程,而后App这个Module依赖于这个基础Library工程,发现如下难题:组件化

  1. 基础库可能不按规范不在特定的package下;
  2. 基础库可能和特定业务结合的相对紧密,没法直接移动;
  3. 移动基础库到单独的Library使用AS工具可能移动不完整;
  4. 一个类可能引用了若干个类,几层引用下来,工做量远超想象;

所以当我刚刚迈出第一步的时候个人心里就已是这样的:ui

安慰本身万事开头难嘛,我作了如下事情:url

  1. 先收集、移动各个不在特定package的基础类到特定package;
  2. 将基础库和特定业务隔离;
  3. 整理、精简基础类;

而后我就开始当心翼翼移动基础类到单独的基础Library,这个过程也十分虐心,由于利用AS的Move功能并不必定保险,强烈建议设计

  • 在单独的分支作组件抽离,作好版本控制,频繁备份,随时还原,以防某一个类找不到致使的Build失败导致须要从头开始(必定要注意);
  • Move一次,及时Build一次,能够及时发现那个类没有Move完整;

这样你就觉得解决了全部问题?Naive!!版本控制

  • 部分基础库,例如网络请求,涉及地方、关联的类实在太多,屡次的Move功能使用也没有彻底将其移动完毕,Move完毕以后各类Build失败;

当时个人表情是这样的:cdn

实在难以一次Move完毕,因而我换了一种思路:Move基础库的缘由是为了让新建的别的业务Module使用,也就是Library中必须存在这些基础类,那我直接在Library中建立出来不就好了吗?

  • 将难以抽离的基础类使用Rename功能从新命名,而后Copy了一份到Library中;
  • 以后将模块移出来的时候一定找不到以前的基础类,咱们将报错的地方改到如今的引用;

对于难以移出的基础类咱们项目确实是这么作的,效率明显更高!

三、路由的设计

3.1 为何须要路由

明确一个问题:各个业务模块之间不会是相互隔离而是必然存在一些交互的;

  • 在Module A须要跳转到Module B某界面,而咱们通常都是使用强引用的Class显式的调用;
  • 在Module A须要调用Module B提供的方法,例如别的Module调用用户模块退出登陆的方法;

这两种调用形式你们很容易明白,正常开发中你们也是绝不犹豫的调用。可是咱们在组件化开发的时候却有很大的问题:

  • 模块B的Activity Class在本身的Module B,那Module A必然引用不到,显式跳转行不通;
  • 同理,直接去调用某个Module的方法也行不通;

由此:必然一种支持组件化的交互方式,这种交互方式须要支持UI跳转以及方法调用的能力,同时处理好多参数及不一样参数类型。

3.2 方式之使用事件通知

备注:此处事件通知指代EventBus或者广播。

这种思路很好想到,在须要交互的地方发通知,而后接收方根据不一样的通知类型作出不一样的处理。相信各位老司机不须要代码也能直接明白。

优势:

  • 简单方便,容易上手;
  • 参数类型很好支持;

缺点:

  • 随着交互的增多须要定义的事件实体类爆炸;
  • 不方便找调用方与接收方;

备注:EventBus只有当业务模块在真实的线上运行阶段是在本身单独的进程才不可用,而这种场景对绝大数App彻底够用

推荐星级:二星级

3.3 方式之一个固定的方法

实现方案:在Activity或者须要暴露的普通类中声明一个统一的方法,这个方法本身去实现,对Activity来讲是去实现UI跳转;对普通类来讲则是去实现功能调用。可是这种方式须要解决两个问题:

  • 跨Module类引用不到
  • 方法签名不固定
  1. 对于跨Module类引用不到:首先须要确认的是跨Module的类确定是引用不到的,那么咱们就给这些类打上标记,间接的就能知道相应的类;标记的形式能够是一个Url,对于Url确定是不区分Module的。例如:我给ActivityA打上一个标记"activitya",而后把这个url做为key,这个类Class做为value使用HashMap存储起来,那么我在别的Module就能直接经过相应的url来获取想要调用的类,而后调用这个特定的方法便可
  2. 方法签名不固定的问题:这个很好理解,我要作不一样的事情那须要的参数无论是个数仍是类型确定是不同的,可是这样的话显然没法作到调用一个固定的方法。这时候咱们仍然能够选择曲线救国:咱们只传递一个参数进去,而这个参数则是一个HashMap,好处则是,能够传递任意个数、类型的参数。这样调用方法的时候你能够将随意多个数、类型的参数传递进去,而后在方法内从HashMap中取出真正须要的参数

缺点:

  • 侵入性太强,任何须要被调用的地方都须要按照统一的格式进行改造;
  • 实现极其不友好,若是我须要和别的Module通讯,那我须要详细的知道传递的参数个数及类型,可是这种实现方式没法明确的像平时方法调用那样被IDE给提示出来;

推荐星级:强烈不推荐,经得起时间检验的方案才是可行的好方案,而这种方式是经不起规模化推广考验的。

3.4 方式之真正的路由

以上两种方式虽然均可以解决问题,可是坦白讲,若是实际用到了项目里的话推动会是极为困难的一件事,由于体验实在是太差了!一个容易被推动、使用体验好的路由应该具有使用方便、上手成本低,改形成本小等基本素质,那么分摊下来应该具体体如今这几点上:

  • 针对UI跳转
    • 改形成本低,不为跳转再重写方法
    • 全部参数类型均支持传递
  • 针对Module间交互
    • 能够清晰的知道方法签名,直接被IDE提示,和普通的方法调用没有区别,调用者一目了然

来看下实际的解决方案:

  1. 对于Activity,咱们也是给它打上一个标记,一个Activity对应一个Url,而后处理好参数的传递问题便可
  2. 对于Module间调用,咱们在Library工程中建立出每一个Module须要向外提供能力的接口,而后每一个Module本身去实现对应的实现类;而且也使用HashMap将这个接口与实现类进行保存,这样在别的Module就能够根据在Library中存在的接口获取到真正的实现类,而方法调用的时候就是简单的调用一个对象的方法,IDE提示很友好,并且不限制方法签名哦

推荐星级:强烈推荐!!备注:具体的路由实现以后会有专门的文章

四、业务组件的剥离

在路由的侵入达到必定程度以后就要作业务组件的剥离,须要注意几点:

4.1 先决条件

  1. Library库抽离或者准备完毕
  2. 路由框架侵入要靠前

这两项属于基础设施,不能边开展边作业务组件的剥离,否则必定会万分痛苦:各类报错,各类Build不过影响工做。

4.2 业务剥离的准则

首先须要明确对于不一样的项目、要求以及不一样的资源分配,业务剥离的程度也是不同的。

  • 最好按照产品功能进行划分,由于自己就是相互之间就有关联,并且代码也可能在同一个package下,方便一块儿Move
  • 剥离的颗粒度由粗到细,组件化初期能够先粗粒度的剥离,快速验证组件化方案以及踩坑,稳定以后再细粒度拆分

4.3 共享数据的组件

业务组件实现单独运行是能够的,可是实际上不少状况下本身独立运行是跑不起来的,举个例子:大多数业务都会和用户体系挂钩,那么缺少用户体系的业务组件步履维艰

那么比较好的作法就是在技术组件剥离以后,优先把共享数据的组件(例如用户组件)先剥离出来,而后别的组件须要共享数据的时候就能够直接依赖于这个组件便可

再写下去,本文篇幅就过长不利于吸取了,别的主题咱们下篇文章接着聊!

广告时间

今日头条各Android客户端团队招人火爆进行中,各个级别和应届实习生都须要,业务增加快、日活高、挑战大、待遇给力,各位大佬走过路过千万不要错过!

本科以上学历、非频繁跳槽(如两年两跳),欢迎加个人微信详聊:KOBE8242011

欢迎关注
相关文章
相关标签/搜索