在平时的开发过程当中,随着项目需求的增长,app支持功能愈来愈多,若是没有组件化的思想,代码会愈来愈臃肿。致使的问题也会愈来愈明显,好比一个很小的需求变化,可能会致使代码的“牵一发动全身”问题,甚至会出现不少隐藏的bug。还会致使维护成本愈来愈高,团队协做低效,问题边界不清晰。java
因而,不少团队开始有了代码解耦的想法,可是面对如此复杂的项目,又不敢轻易变动其中的代码结构,如何顺利的解耦就成了不少团队难以入手的问题。甚至因为业务的不断迭代,致使代码解耦的问题遥遥无期。android
固然,做为一位合格的App架构师,遇到再难的问题也会迎难而上。因而便开始对APP总体项目结构进行分析,制定解耦方案。一般状况下,解耦的最佳思路就是根据业务边界对代码结构进行划分。好比说某APP里面包含了IM、直播、内容展现等等业务场景,因而从架构的角度来讲,整个APP的架构应该是以下图所示:git
这种架构思路上很清晰,对应到咱们Android代码结构,就是根据这些业务边界,拆分红不一样的module,module之间没有直接的引用和依赖,代码彻底解耦。做为团队开发成员也有很清晰的业务边界,代码维护成本大大下降,开发效率也会明显提升,应该是一个很不错的方案。github
所谓的组件化其实就是根据业务边界对代码进行解耦,不一样的业务对应不一样的module,这些module相互独立,能够独自做为一个app进行开发并独立运行,合并时能够打包到一个总体的app中,实现完整的app功能。算法
那么问题来了,以上的架构的确是很是不错的选择,可是实际的业务中,很难有个清晰的边界,而且业务与业务直接总会有衔接的地方。若是使用以上的架构,那么这些不一样的module之间又该如何进行调用呢?数据库
在咱们Android系统中,进程是一个独立程序,每一个进程都具备本身的虚拟机 (VM),应用代码是在与其余应用隔离的环境中运行,进程直接的通讯主要是基于Binder机制。为何要提Binder,首先Binder是Android系统的中很是重要的实现机制,而咱们组件化代码耦合的问题也能够借鉴其实现原理。接下来我简单的介绍一下Binder机制,先整体看一下Binder架构图:bash
能够看出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群中@我进行交流。接下来咱们再回到“如何优雅的进行组件化”这个问题上。app
在了解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,完成跨Module自动注入流程,可参考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框架,API使用很是简洁),其实组件化并非一个版本就须要完成的。组件化的第一步固然仍是根据业务边界来架构本身的APP框架,不一样的Module中声明好本身的服务。而组件化的工做能够拆分到不一样的迭代版本中,对于新增的业务明确到对应的业务Module中开发,对应老的业务代码若是耦合度比较高的,不建议直接修改逻辑,能够先将这部分代码耦合的地方抽象到服务接口中,经过服务调用来实现调用过程。并在将来的版本中逐步进行剥离解耦最终实现真正的代码隔离,以服务的形式完成业务间的交集部分。
前面讲了不少,咱们再回到主题,相信你们对于“如何实现本身的Android组件化改造”应该有了很清晰的步骤和流程来吧。此外仅表明我的的一些拙见,若是意见或者建议欢迎进ARetrofit QQ群赐教。