如何优雅的实现自己的Android组件化改造?

作者 : 开心源码 本文共4819个字,预计阅读时间需要13分钟 发布时间: 2022-05-12 共175人阅读

本篇文章的主要目的:

  • 帮助正在对项目进行组件化改造或者者想建立组件化项目架构的小伙伴,更好的认识组件化本质。
  • 目前组件化的框架众多,说的天花乱坠的,其本质来说其实都差不多,阅读本文以后,读者甚至可以摒弃这些开源框架,根据自己的项目特点,轻松构建自己的组件化框架。
  • 帮助想学习和理解组件化框架,并尝试动手写自己的开源框架的小伙伴们

什么是组件化?

在平常的开发过程中,随着项目需求的添加,app支持功能越来越多,假如没有组件化的思想,代码会越来越臃肿。导致的问题也会越来越显著,比方一个很小的需求变化,可能会导致代码的“牵一发动全身”问题,甚至会出现很多隐藏的bug。还会导致维护成本越来越高,团队协作低效,问题边界不清晰。

于是,很多团队开始有了代码解耦的想法,但是面对如此复杂的项目,又不敢轻易变更其中的代码结构,如何顺利的解耦就成了很多团队难以入手的问题。甚至因为业务的不断迭代,导致代码解耦的问题遥遥无期。

当然,作为一位合格的App架构师,遇到再难的问题也会迎难而上。于是便开始对APP整体项目结构进行分析,制定解耦方案。通常情况下,解耦的最佳思路就是根据业务边界对代码结构进行划分。比方说某APP里面包含了IM、直播、内容展现等等业务场景,于是从架构的角度来说,整个APP的架构应该是如下图所示:

图1

这种架构思路上很清晰,对应到我们Android代码结构,就是根据这些业务边界,拆分成不同的module,module之间没有直接的引用和依赖,代码完全解耦。作为团队开发成员也有很清晰的业务边界,代码维护成本大大降低,开发效率也会显著提高,应该是一个很不错的方案。

所谓的组件化其实就是根据业务边界对代码进行解耦,不同的业务对应不同的module,这些module相互独立,可以独自作为一个app进行开发并独立运行,合并时可以打包到一个整体的app中,实现完整的app功能。

如何优雅的进行组件化?

那么问题来了,以上的架构确实是非常不错的选择,但是实际的业务中,很难有个清晰的边界,并且业务与业务直接总会有衔接的地方。假如使用以上的架构,那么这些不同的module之间又该如何进行调用呢?

在我们Android系统中,进程是一个独立程序,每个进程都具备自己的虚拟机 (VM),应用代码是在与其余应用隔离的环境中运行,进程直接的通信主要是基于Binder机制。为什么要提Binder,首先Binder是Android系统的中非常重要的实现机制,而我们组件化代码耦合的问题也可以借鉴其实现原理。接下来我简单的详情一下Binder机制,先总体看一下Binder架构图:

Binder进程间通信机制.png

可以看出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,用以下这张架构图来说明:

module通信架构

上图中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中开发,对应老的业务代码假如耦合度比较高的,不建议直接修改逻辑,可以先将这部分代码耦合的地方笼统到服务接口中,通过服务调用来实现调用过程。并在未来的版本中逐渐进行剥离解耦最终实现真正的代码隔离,以服务的形式完成业务间的交集部分。

组件化最佳实践

  • ARetrofit原理

小结

前面讲了很多,我们再回到主题,相信大家对于“如何实现自己的Android组件化改造”应该有了很清晰的步骤和流程来吧。此外仅代表个人的少量拙见,假如意见或者者建议欢迎进ARetrofit QQ群赐教。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 如何优雅的实现自己的Android组件化改造?

发表回复