Android 架构分层中的模块、组件、插件,组件间通信(路由等)

模块化、组件化、插件化。

计算机界有一句名言:“计算机科学领域的任何问题都可以通过增加一个中间层来解决。
-- 页面组件化的设计思路是:
 1.将页面拆分为粒度更小的组件,组件内部除了包含UI实现,还包含数据层和逻辑层;
 2.组件提供个性化配置满足两端差异需求,如果无法满足再通过代理抛到上层处理。
页面组件化是一个良好的设计,但它主要适用于解决Activity巨大化的问题。由于底层差异巨大的情况,使得页面组件化很难实现大规模的复用,复用效率低。另一方面,页面组件化也没有为2端差异性预留可伸缩的空间。

 1.Android 插件化 —— 指将一个程序划分为不同的部分,比如一般 App 的皮肤样式就可以看成一个插件;
 2.Android 组件化 —— 这个概念实际跟上面相差不那么明显,组件和插件较大的区别就是:组件是指通用及复用性较高的构件,比如图片缓存就可以看成一个组件被多个 App 共用;
 3.Android动态加载 — 这个实际是更高层次的概念,也有叫法是热加载或 Android 动态部署,指容器(App)在运⾏状态下动态加载某个模块,从而新增功能或改变某⼀部分行为. 

  组件化实际上是一种编程思想,而插件化是一种实实在在的技术,组件化是为了代码的高度复用而出现的,我们可以通过把不同模块的业务做成一个个独立的 Library ,可以单独对这些 Library 进行版本管理,从而可以供给给想使用它的一切 Apk,这样做的好处不仅可以提高代码的复用性,而且也可以帮助项目进行业务解耦,提升开发效率。
  插件化是为了解决应用越来越庞大、占用内存越来越高、Apk 体量过大、解决65535方法数等复杂问题而出现的,插件化会把各个解耦业务单独的封装到 APK 插件中,通过插件附属到宿主 APK 中,从而完成功能的实现,这些独立的 APK 插件甚至可以单独打包成应用。比如手机淘宝,它不仅有淘宝这个宿主应用本身的一些功能,还有一些像聚划算、书城这类的其他应用也需要接入进来,那么如果没有插件化,像聚划算的开发人员就要同时维护两套代码,一套是自己的聚划算项目,一套是接入到手淘的聚划算项目,出现了Bug也得同时去两套代码去更改,这在管理和开发效率上就会非常受限。但如果我们把聚划算做成一个插件,那么哪个应用要接入聚划算,只要把这个 APK 包接入进来即可,在代码中的就是一些 compile 和 gradle 的配置,插件内部的代码完全不用你操心了。

-- 严选 Android 组件化实践- https://www.jianshu.com/p/a503db59c6eb
页面组件化 ht-router 接入, DeepLink从认识到实践,接入杭研 ht-router;ARouter、天猫统跳、ActivityRouter 等。如 ARouter,通过 path 定义 group 和跳转目标,而严选工程以 host 标识跳转目标,也有些差异。scame

-- 项目模块化(组件化)
a lightweight and simple routing framework that provides jump routing and method routing- https://github.com/michaelxs/Android-XRouter
项目模块化(组件化),插件化和模块化- https://github.com/SpinyTech/ModularizationArchitecture
Android 客户端路由框架的整理和思考- https://www.jianshu.com/p/15d8cc6cf19b
局域网路由解决单进程项目模块化,广域网路由解决多进程项目模块化。
包含的业务模块也越来越多,模块之间耦合的程度也越来越高。用路由机制来实现模块化解耦.路由框架和模块拆分。

模块间通信框架-Hub: https://github.com/SilenceDut/Hub

-- 路由框架:
支持自由组装不同的 module 实现差异化编译
提供页面路由和拦截器
提供方法路由包括:同步和异步

  关于路由机制,熟悉iOS开发的朋友可能并不陌生,在iOS上有很多架构方案都是采用路由机制来时间模块之间的解耦的,比如VIPER(View Interactor Presenter Entity Routing)思想等等。其实思路都是相同的,Android上面组件化也是通过公用的路由,来实现模块与模块之间的隔离。

-- 模块化,组件化与MVP

浅谈项目重构之路——模块化- https://www.jianshu.com/p/e40b7eb99573
浅谈项目架构重构之路——组件化与MVP- https://www.jianshu.com/p/1a1ddecb576d
模块化架构开发,使用模块化能解决什么问题:
 解决由于模块边界定义不清而导致的耦合问题
 统一规定模块之间通信方式,去除过分使用EventBus而臃肿的event包
 隔离各个模块代码,利于并行开发测试
 可单独编译打包某一模块,提升开发效率
 模块实现可复用,快速集成影子App
 开发时,可以进行单业务编译,避免全量编译耗时过长

 组件和模块在英文翻译里都被叫为module,而在AS中lib模块都被定义为module。
 组件,翻译成module并不准确,他其实是一个通用的Lib,只不过组件在AS中的实现,多数以module的形式实现。在Android App中,组件应该是构成业务模块或业务功能的基本单位。
  模块 = 组件A + 组件B + …… 组件B

模块化,三个技术难点。
隔离模块边界;
模块间的跳转;
模块间的通信;

一个良好的项目周期,需要适时推动一些重构计划,提高代码质量,而并不是只停留在业务代码层次。
模块化的架构不仅解决了模块耦合问题,同时也调高了整个App的拓展性与维护性。

> 组件间通信包括两个场景:(1)UI 跳转;(2)调用组件某个类的某个方法
阿里 ARouter组件间通信- https://github.com/alibaba/ARouter
android 路由框架- https://github.com/sarlmoclen/SRouterDemo
- 支持依赖注入注解,可单独作为依赖注入框架使用;支持添加多个拦截器,自定义拦截顺序;
Android-Demos/LiteRouter/- https://github.com/hiphonezhu/Android-Demos/tree/master/LiteRouter

阿里页面路由ARouter- https://github.com/XinRan5312/QXFirstARouter
AbstractProcessor Test Demo- https://github.com/zyao89/DemoAbstractProcessor
阿里ARouter使用及源码解析- https://www.jianshu.com/p/46d174f37e82
  通过注解处理器RouteProcessor生成的,关于如何自定义注解处理器,可以阅读Android编译时注解APT实战(AbstractProcessor)https://www.jianshu.com/p/07ef8ba80562,同时也需要学习JavaPoet的基本使用。
  那些在运行时(Runtime)通过反射机制运行处理的注解,而是讨论在编译时(Compile time)处理的注解。
  ARouter 的初始化只做了一件事,找到自己编译期产生的清单文件,把 Group 、Interceptor 、Provider 三种清单加载到
Warehouse 内存仓库中。

支持增量编译和组件化开发的路由框架Pigeon- https://github.com/bboylin/Pigeon

-- WMRouter:美团外卖Android开源路由框架,WMRouter主要提供URI分发、ServiceLoader两大功能。
WMRouter是一款Android路由框架,基于组件化的设计思路,最初用于解决美团外卖C端App在业务演进过程中的实际问题
WMRouter- https://github.com/meituan/WMRouter
 WMRouter路由框架,借鉴网络请求的思想,设计了基于UriRequest、UriHandler、UriInterceptor的URI分发机制,在保证功能灵活强大的同时,又尽可能的降低了使用难度;另一方面,借鉴SPI的设计思想、Java和美团平台的ServiceLoader实现,开发了自己的ServiceLoader模块,解决外卖平台化过程中的四个问题(通信问题、复用问题、依赖注入、编译问题)。
  原有的单个工程拆分成多个工程,就不可避免的涉及到多工程之间的耦合问题,主要包括通信问题、复用问题、依赖注入、编译问题.
WMRouter:美团外卖Android开源路由框架- https://blog.csdn.net/MeituanTech/article/details/82108690

  架构里面提供了二种平级间的通信方式:scheme路由和美团自建的ServiceLoaders sdk。scheme路由本质上是利用Android的scheme原理进行通信,ServiceLoader本质上是利用的Java反射机制进行通信。

> 插件化
- 我的Android重构之旅:插件化篇- https://blog.csdn.net/c6E5UlI1N/article/details/81125532

  插件化踩坑之路——Small和Atlas方案,阿里另一款热修复方案 Andfix。
  Small 是一款轻量级的跨平台插件化框架,更侧重于业务的解耦,组件化开发。所有的插件支持内置于宿主包中,并且高度透明,插件编码、布局编写方式、调试与独立整包开发无异,通过 URL 来进行宿主与插件之间的通信和传递参数。

BsDiff_And_Patch差分算法- https://github.com/hongyangAndroid/BsDiff_And_Patch

> 项目组件化
  那么什么是组件化?概括为:组件化是基于可重用目的将项目按照具体业务需求进行拆分,并能将拆分得到的组件进行灵活重组,减小耦合(业务需求上的拆分)。
 组件化:随着业务量的不断增长,app也会不断的膨胀,开发团队的规模和工作量也会逐渐增大,面对所衍生的64K问题、协作开发问题等,app一般都会走向组件化。组件化就是将APP按照一定的功能和业务拆分成多个组件module,不同的组件独立开发,组件化不仅能够提供团队的工作效率,还能够提高应用性能。而组件化的前提就是解耦,那么我们首先要做的就是解耦页面之间的依赖关系。
 Native与H5的问题:现在的APP很少是纯Native的,也很少会有纯H5的,一般情况下都是将两者进行结合。这时候就需要非常便捷并且统一的跳转方案,因为在H5中是无法使用StartActivity()跳转到Native页面的,而从Native跳转到H5页面也只能通过配置浏览器的方式实现。

简单聊聊Android Architecture Componets(组件设计)- https://github.com/googlesamples/android-architecture-components
组件化项目- https://github.com/JessYanCoding/ArmsComponent
Android组件化方案- http://blog.csdn.net/guiying712/article/details/55213884
Android 组件化探索与思考- https://github.com/WuXiaolong/ModularSample
Android项目组件化AndroidModulePattern(阿里ARouter作为路由)- https://github.com/guiying712/AndroidModulePattern
Router activities and methods with url for android- https://github.com/mzule/ActivityRouter
Android组件化方案- http://blog.csdn.net/guiying712/article/details/55213884
Android组件化之终极方案- http://blog.csdn.net/guiying712/article/details/78057120
Android组件化和插件化开发- https://www.cnblogs.com/android-blogs/p/5703355.html

-- 模块化是一种指导理念,其核心思想就是分而治之、降低耦合。组件化的目标之一就是降低整体(app)与器官(组件)的依赖关系,缺少任何一个器官app都是可以存在并正常运行的。
  组件化和插件化的最大区别(应该也是唯一区别)就是组件化在运行时不具备动态添加和修改组件的功能,但是插件化是可以的。 
-- 要实现组件化,不论采用什么样的技术路径,需要考虑的问题主要包括下面几个: 
 * 代码解耦。如何将一个庞大的工程拆分成有机的整体? 
 * 组件单独运行。上面也讲到了,每个组件都是一个完整的整体,如何让其单独运行和调试呢? 
 * 数据传递。因为每个组件都会给其他组件提供的服务,那么主项目(Host)与组件、组件与组件之间如何传递数据? 
 * UI跳转。UI跳转可以认为是一种特殊的数据传递,在实现思路上有啥不同? 
 * 组件的生命周期。我们的目标是可以做到对组件可以按需、动态的使用,因此就会涉及到组件加载、卸载和降维的生命周期。 
 * 集成调试。在开发阶段如何做到按需的编译组件?一次调试中可能只有一两个组件参与集成,这样编译的时间就会大大降低,提高开发效率。 
 * 代码隔离。组件之间的交互如果还是直接引用的话,那么组件之间根本没有做到解耦,如何从根本上避免组件之间的直接引用呢?也就是如何从根本上杜绝耦合的产生呢?只有做到这一点才是彻底的组件化。

某个组件module的build.gradle的示例:
if(isRunAlone.toBoolean()){    
  apply plugin: 'com.android.application'
}else{  
  apply plugin: 'com.android.library'
}
.....
resourcePrefix "readerbook_"
  sourceSets {
        main {
            if (isRunAlone.toBoolean()) {
                manifest.srcFile 'src/main/runalone/AndroidManifest.xml'
                java.srcDirs = ['src/main/java','src/main/runalone/java']
                res.srcDirs = ['src/main/res','src/main/runalone/res']
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
            }
        }
    }

支持组件的代码资源隔离、单独调试、集成调试、组件交互、UI跳转、生命周期等完整功能。
android组件化方案- https://github.com/mqzhangw/JIMU

-- Android 组件化 - 路由设计
 页面跳转这方面,比如阿里的ARouter, 天猫的统跳协议, Airbnb的DeepLinkDispatch, 借助注解来完成页面的注册,从而很巧妙地实现了路由跳转。界面解耦又是App架构的重中之重。 统跳协议是天猫App统一跳转协议,主要负责天猫App界面之间的串联,也就是界面跳转服务。
 爱奇艺 Andromeda开源地址- https://gitee.com/bettar/Andromeda.
 Airbnb DeepLinkDispatch- https://github.com/airbnb/DeepLinkDispatch
 爱奇艺发布重磅开源项目- https://blog.csdn.net/Ch97CKd/article/details/80522954
  目前爱奇艺App中,由于复杂的业务场景,,导致既有单进程的通信需求,也有跨进程的通信需求,并且还要支持跨进程通信中的Callback调用,以及全局的事件总线。
 Andromeda:架构的核心就是Dispatcher和RemoteTransfer, Dispatcher负责管理所有进程的业务binder以及各进程中RemoteTransfer的binder; 而RemoteTransfer负责管理它所在进程所有Module的服务binder.
 - 微信Android模块化架构重构实践(上)中说到的那样,"我们理解的协议通信,是指跨平台/序列化的通信方式,类似终端和服务器间的通信或restful这种。现在这种形式在终端内很常见了。协议通信具备一种很强力解耦能力,但也有不可忽视的代价。无论什么形式的通信,所有的协议定义需要让通讯两方都能获知。通常为了方便会在某个公共区域存放所有协议的定义,这情况和Event引发的问题有点像。另外,协议如果变化了,两端怎么同步就变得有点复杂,至少要配合一些框架来实现。在一个应用内,这样会不会有点复杂?用起来好像也不那么方便?更何况它究竟解决多少问题呢"。
 - 饿了么Hermes:原理则是利用动态代理+反射的方式来替换AIDL生成的静态代理,但是它在跨进程这方面本质上采用的仍然是bindService()的方式.
饿了么 Hermes的demo请点击- https://github.com/Xiaofei-it/Hermes-IPC-Demo
简单易用的安卓进程间通信IPC框架- https://github.com/Xiaofei-it/Hermes
 - 其他组件间通信方案:DDComponentForAndroid,ModularizationArchitecture
DDComponentForAndroid- https://gitee.com/andli/DDComponentForAndroid
ModularizationArchitecture- https://github.com/wutongke/ModularizationArchitecture
项目Github地址:https://github.com/SpinyTech/ModularizationArchitecture

> 模块化 (自定义路由框架)
  曾经也许你做过这样的事,将一些公共的代码组成独立的 module 并编译为 library 供各个子模块或其他项目引用,通常利用这种方式实现项目模块化(小功能独立拆分)。
 页面路由模块化的页面路由功能- https://github.com/chiclaim/android-modularization
 - Android 模块化探索与实践- https://www.cnblogs.com/baronzhang/p/6861258.html
 关于Android模块化我有一些话不知当讲不当讲- https://www.jianshu.com/p/910911172243
 - 浅谈Android模块化设计(常规思路)- http://blog.csdn.net/weijianfeng1990912/article/details/66475959
 模块化设计轻量级的方案,手淘Atlas和蚂蚁金服支付宝MPass这样大型的容器化方案。模块化的精髓,个人觉得应该就是解耦,所有才有了路由的出现。
 在Android领域的模块化方案,基本都逃不开路由的设计,基本原理:都是用到了intent-filter中的data的host和scheme,借鉴了网络中概念,通过类似url地址的信息和Activity建立路由映射关系,然后通过,url地址来请求,经过建立的路由映射关系,根据host,scheme进行处理,跳转对应模块。 所以在Android模块化方案中,大部分设计套路都集中在了 怎么建立这种路由表了。

-- Android 组件化之路
  模块化编程和结构化编程与面向对象编程是密切相关的,它们的目的都是将大型软件程序划分成一个个更小的部分。模块化编程的粒度会更“粗”一些。在Java9中也在编译器层面提供了模块化的支持:Java Platform Module System 。
  组件是一个类似的概念,但通常指更高的级别;组件是整个系统的一部分,而模块是单个程序的一部分。“模块”一词因语言而有很大差异;在Python中,它非常小,每个文件都是一个模块,而在Java 9中,它是非常大的,其中模块是包的集合,包又是文件的集合。
  在面向对象编程中,通常使用接口作为模块间通信的桥梁,也就是基于接口的编程。

  不管是模块化还是组件化,它们都有一个共同的目标:将一个大的软件系统细化成一个个模块或者组件,都是为了重用和解耦。
  网上很多文章对于组件和模块的定义也是不尽相同的,一些人认为组件的粒度更细,它只是具备单一功能与业务无关的组件,比如一个日历选择控件就认为是一个组件。而模块他们认为就是业务模块,顾名思义,就是按业务划分而成的模块。而另一部分人则相反。

对组件和模块做出以下定义:
 组件:侧重于业务,可编译成单独的app,一般只负责单一业务,具备自身的生命周期(通常包含Android四大组件的一个或多个,所以称之为组件也更加贴切)
 模块:侧重于功能,与业务无关,比如自定义控件、网络请求库、图片加载库等

 怎么做到既能让组件间通信又高度解耦呢?这就需要用到文章开头提到的面向接口编程思想和依赖注入(或者叫依赖查找)技术。

实现原理不外乎两种:
  一种是将分布在各个组件的类按照一定的规则在内部生成映射表,这个映射表的数据结构通常是一个Map,Key是一个字符串,Value是一个类或者是类的路径名称(用于通过反射进行类的实例化)。通俗来说就是类的查找,这种实现方式要求调用方和被调用方都持有接口类,通常这些共同持有的接口类会被定义在一个Common基础模块中,而且在运行时这些相互通信的组件必须打包到同一个APK中。这种实现方式导致无法真正实现代码隔离(需要通信的两个组件仍然是存在依赖关系的),基于这种原理实现的组件化架构“自约束能力”很弱,因为无法约束开发人员通过直接引用的方式进行通信的行为,虽然一开始设计人员想的很美好,但是开发人员在实现时做出来的产品却不是那样,因为“自约束能力”弱的架构设计是通过“编码规范”、“测试驱动”甚至是“人员熟练度”来保证开发人员实现的代码符合设计人员的设计初衷,而且这种架构也无法保证后续接手维护项目的开发人员能够贯彻原本的设计思想,随着时间推移,项目往越来越糟糕的方向演进(解决这个问题最好的方案就是从编译器层面进行约束,也就是把问题拦截在编码阶段,然而Java9才支持模块化开发,Android目前还处于支持部分Java8的特性的阶段,路还很长)。
  另一种方案是基于事件总线的方式实现组件之间的通信,不再是面向接口编程,而是面向通信协议编程,可以理解为组件间的调用类似http请求。这些框架会在内部建立跨进程通信的连接(也就是事件总线),这条事件总线负责分发路由请求以及返回执行结果。这种实现方式的好处是真正可以实现代码隔离,组件可以运行在独立的进程中,但是只支持基本类型参数的转发。实现跨进程通信有很多方案,比如Android原生的四大组件、Socket、FileObserver、MemoryFile、基于AIDL的Messager等等,使用Android原生的好处是安全性方面的工作由Android帮我们完成了,而使用Socket则需要自己实现加密Socket。

  第一种方案适合小型的项目,因为这些项目通常都是单进程的,虽然这样设计的架构“自约束能力”弱,但是目前大多数Android项目团队开发人数也不会太多,所以管理难度较小,而第二种实现方案则更适合跨进程组件化的项目(组件一般运行在独立的进程中甚至一个组件就是一个APP)。
  在我看来Android的组件化是存在3个阶段的,第一个是从单工程项目过度到多模块的阶段;第二个是从多模块过度到多组件的阶段;第三个就是多组件独立进程的阶段。而目前大多数应用其实都是在第二个阶段或者介于第二和第三个阶段之间,所以对于这样的项目,选择一个既支持类查找方式,又支持事件总线的组件化框架是最合适的(这也是一开始设计KRouter想要达到的效果,虽然目前暂时不支持跨进程组件。。。)
  在项目实施组件化过程中,其实真正耗费时间、精力的不是编码,而是一开始组件的划分以及组件单元测试的代码的编写。有可能因为一开始对业务的不熟悉,导致后期开发时发现组件划分的不够准确,需要加以调整;或者是对接口抽象的不够好,导致维护时频繁修改接口;还有可能在编写单元测试时觉得枯燥乏味而选择放弃。我们不能因为遇到这些困难就半途而废,或者是质疑自己的架构设计能力,没有哪一个架构设计是放之四海皆准的,有可能一个项目的架构设计放在另一个项目中就显得不那么合适了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值