帮助正在对项目进行组件化改造或者想创建组件化项目架构的小伙伴,更好的认识组件化本质。android
目前组件化的框架众多,说的天花乱坠的,其本质来讲其实都差很少,阅读本文之后,读者甚至能够摒弃这些开源框架,根据本身的项目特色,轻松构建本身的组件化框架。git
帮助想学习和了解组件化框架,并尝试动手写本身的开源框架的小伙伴们github
在平时的开发过程当中,随着项目需求的增长,app支持功能愈来愈多,若是没有组件化的思想,代码会愈来愈臃肿。致使的问题也会愈来愈明显,好比一个很小的需求变化,可能会致使代码的“牵一发动全身”问题,甚至会出现不少隐藏的bug。还会致使维护成本愈来愈高,团队协做低效,问题边界不清晰。算法
因而,不少团队开始有了代码解耦的想法,可是面对如此复杂的项目,又不敢轻易变动其中的代码结构,如何顺利的解耦就成了不少团队难以入手的问题。甚至因为业务的不断迭代,致使代码解耦的问题遥遥无期。数据库
固然,做为一位合格的App架构师,遇到再难的问题也会迎难而上。因而便开始对APP总体项目结构进行分析,制定解耦方案。一般状况下,解耦的最佳思路就是根据业务边界对代码结构进行划分。好比说某APP里面包含了IM、直播、内容展现等等业务场景,因而从架构的角度来讲,整个APP的架构应该是以下图所示:bash
这种架构思路上很清晰,对应到咱们Android代码结构,就是根据这些业务边界,拆分红不一样的module,module之间没有直接的引用和依赖,代码彻底解耦。做为团队开发成员也有很清晰的业务边界,代码维护成本大大下降,开发效率也会明显提升,应该是一个很不错的方案。网络
所谓的组件化其实就是根据业务边界对代码进行解耦,不一样的业务对应不一样的module,这些module相互独立,能够独自做为一个app进行开发并独立运行,合并时能够打包到一个总体的app中,实现完整的app功能。架构
那么问题来了,以上的架构的确是很是不错的选择,可是实际的业务中,很难有个清晰的边界,而且业务与业务直接总会有衔接的地方。若是使用以上的架构,那么这些不一样的module之间又该如何进行调用呢?app
在咱们Android系统中,进程是一个独立程序,每一个进程都具备本身的虚拟机 (VM),应用代码是在与其余应用隔离的环境中运行,进程直接的通讯主要是基于Binder机制。为何要提Binder,首先Binder是Android系统的中很是重要的实现机制,而咱们组件化代码耦合的问题也能够借鉴其实现原理。接下来我简单的介绍一下Binder机制,先整体看一下Binder架构图:框架
能够看出Binder是一个典型的CS架构,进程间的通讯基于ServiceManager这个大管家,Client进程从ServiceManager中获取Server进程的一个远程代理,进行通讯。为了让你们更直接的理解,我从代码层面上来简单描述一下这个过程。好比咱们启动一个Activity时,须要ActivityManagerService(AMS)这个服务来进行管理,而AMS运行在SystemServer中,那么如何获取这AMS呢,咱们从源码来分析(如下源码android-28中):
public static IActivityManager getService() { return IActivityManagerSingleton.get();}private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() { @Override protected IActivityManager create() { //经过ServiceManager.getService获取到AMS的代理IActivityManager final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); final IActivityManager am = IActivityManager.Stub.asInterface(b); return am; }};复制代码
app中经过ServiceManager.getService获取到远程进程中服务的代理,只须要指定服务的name就能够。
Binder机制就不展开讲解,若是想了解更多的同窗,能够加入到ARetrofit的QQ群中@我进行交流。接下来咱们再回到“如何优雅的进行组件化”这个问题上。
在了解Binder机制后,对于咱们组件化过程当中解耦代码如何通讯,其实也能够采用相似的机制。固然咱们的module之间并无跨进程,不会有跨进程通讯的问题。
咱们能够将图1中的每个module想象成一个服务,那么module之间的通讯其实就相似于服务之间的通讯。而服务之间的通讯其实也不须要直接进行类的引用,只须要拿到另外一个module的服务代理,经过这个代理进行通讯。这里又出了一个新的问题,如何实现提供代理服务的代理管家呢,其实这也相似于Binder里面的ServiceManager,用如下这张架构图来讲明:
上图中IM module注册本身的IM服务到ServiceManager中,直播 module想要和IM module只须要获取IM的服务代理就能够进行操做。注册的过程,对应到实际的代码中,其实就是中ServiceManager这个管家的Module中声明本身的服务接口,并在本身的module中实现这一接口。其余module只须要在运行时拿到接口实现类的实例对象就能够完成这一过程了,这一部分其实能够参考ARetrofit README中 四 高阶用法
中登陆服务接口的声明与注册过程就能够了解。
固然,这篇文章不只仅让你们了解别人已经开源好的框架,其实相似的框架不少,如ARetrofit 、ARouter、CC等开源,无关star量,这都是开源迟早和推广的问题,其实本质上都是基于以上的原理,区别就是上层的封装的问题,最终呈现的API是否简洁,是否符合本身的要求,可否直观的进行代码维护。
这里我将带着你们动手一块儿实现本身的ServiceManager管家。
第一步,咱们在ServiceManager中注册不一样module的服务接口,以下:
public interface ILoginManager { void login(); User getUser();}复制代码
第二步,在Login Module中实现该接口,以下:
@Inject //须要自动注入服务的声明public class LoginManagerService implement ILoginManager { @Override void login(Activity activity) { Intent intent = new Intent(activity, LoginActivity.class); activity.startActivity(intent); } @Override User getUser(CallBack callback) { //...网络或者 或者 本地数据库 等回去异步返回或者同步返回结果 }}复制代码
第三步, 在直播 Module中跨Module获取ILoginManager服务实例对象,这里须要经过Android Studio自动注入框架[AInject]AInject(https://github.com/yifei8/AInject),完成跨Module自动注入流程,可参考(https://github.com/yifei8/AInject)[AInject]用法,具体实现以下:
public class ServiceManager implements InjectContract { private static class ServiceManager { private static final ServiceManager instance = new ServiceManager(); } static ServiceManager getInstance() { return InstanceHolder.instance; } /** * @Fixme 这里建议使用实现LRU算法的列表存储 */ List<Object> services = new ArrayList(); /** * * "@Inject" 注解标示的class 最终都会注入到该"@IMethod"注解标示过的方法中 * 注:"@IMethod"注解标示过的方法将由编译器自动注入实现代码,注入最终的代码以下如: * * @IMethod * public void iMethodName() { * injectClass("injectClassName1") * injectClass("injectClassName2") * injectClass("injectClassName3") * injectClass("injectClassName4") * } * * 用户能够在该方法中经过反射完成本身的业务需求 * @param className class name */ @IMethod public void startInject() { } @Override public void injectClass(String serviceClassName) { services.clear() services.add(className); } /** * 获取登陆服务,可在任意Module直接获取该服务的实例化对象 */ public static ILoginManager getILoginManager() { if (getInstance().service.size() == 0) { getInstance().startInject(); } for (String className: getInstance().services) { try { Class<?> clazz = Class.forName(className); Object obj = clazz.getConstructor().newInstance(); if (obj instanceof ILoginManager) { return obj; } else { obj = null; }} catch (Exception e) { e.printStackTrace(); } } return null; }}复制代码
其实就是这么简单,一个组件化的框架就完成了。
看到这里的小伙伴们,大概已经理解了如何进行解耦Module直接的通讯工做了吧。想一想平时有没有遇到其余关于高耦合的代码须要解耦的,均可以参考这种思路哦。
前面教你们如何进行组件化,已经如何实现组件化,其实还忽略了一个很是重要的问题,就是如何对现有的项目进行组件化。现有的项目通常都已经累计了不少代码量,若是一次性根据业务进行拆解处理,解耦合显然是不合实际的。那么到底该怎么作呢?
其实有了以上自定义的组件化框架ARetrofit,其实组件化并非一个版本就须要完成的。组件化的第一步固然仍是根据业务边界来架构本身的APP框架,不一样的Module中声明好本身的服务。而组件化的工做能够拆分到不一样的迭代版本中,对于新增的业务明确到对应的业务Module中开发,对应老的业务代码若是耦合度比较高的,不建议直接修改逻辑,能够先将这部分代码耦合的地方抽象到服务接口中,经过服务调用来实现调用过程。并在将来的版本中逐步进行剥离解耦最终实现真正的代码隔离,以服务的形式完成业务间的交集部分。
前面讲了不少,咱们再回到主题,相信你们对于“如何实现本身的Android组件化改造”应该有了很清晰的步骤和流程来吧。此外仅表明我的的一些拙见,若是意见或者建议欢迎进ARetrofit赐教。
转载自“安卓巴士开发者门户”