code小生 一个专注 Android 领域的技术平台

公众号回复 Android 加入我的安卓技术群

作者:低情商的大仙
链接:https://www.jianshu.com/p/de432a82ed93
声明:本文已获低情商的大仙授权发表,转发等请联系原作者授权

前言

本文讨论的其实是项目经过模块化后的一种情况,如果没有模块化需求其实是无所谓的。如果项目已经进行了模块化,推荐大家花几分钟看下。

需求

首先,我们来看一张常见的模块化后的图:

项目结构图.png

然后我们有这样一个需求,我希望在app中获取所依赖的所有业务module的名称。
注意,模块化后各个业务模块一般都是可以拆卸的,也就是说随时会增加或减少模块

分析

现在我们分析下有哪些方案:

方案一:App模块中手动维护

public class ModuleName {    private static List moduleName = new ArrayList<>();static {        moduleName.add("businessone");        moduleName.add("businesstwo");    }public static List getModuleName(){return moduleName;    }}

这种方案是最容易想到的,也是最简单的,但有一个问题,如果卸载了某个模块或者新增了一个模块,我们就得手动修改ModuleName这个类,这导致了一个结果: 一个模块的变更会影响另一个模块的功能,这和我们模块化解耦的要求完全不符合,所以肯定不能采用,这样的代码完全无法维护。

方案二:各业务模块主动注入

既然方案一耦合太严重,我们换种思路,将这个moduleName的list放到bottomlibrary中,然后各模块自己往里面添加数据。

首先在bottomlibrary中:

public class ModuleName {    private static List moduleName = new ArrayList<>();public static void addModuleName(String name){        moduleName.add(name);    }public static List getModuleName(){return moduleName;    }}

然后在各模块中手动注册:

ModuleName.addModuleName("businessone");

现在,对比方案一,我们发现耦合严重的问题已经没有了,一个模块不加载或者新增一个模块再也不需要改动其他模块的代码了,但一个新的问题出现了: 我们何时何地调用那行注册名称的代码呢?

要知道,一般业务模块中是没有Application存在的,如果希望在模块加载初始化时就调用的话就必须在各模块中定义自己的Application接口,然后统一由App中的Application调用分发。仔细想想这个过程,和app中获取各模块的名称是不是一个道理?只不过这里是在app中要获取各模块中的Application接口罢了。我们不可能用问题A去解决问题A,所以这样不可取,为了解决这个问题我们看下一个方案。

方案三:服务发现机制

经过了方案二的讨论,我们发现问题出在各模块什么时候将自己的信息注册到统一管理类ModuleName中去,既然主动注册时机不好确定,我们能不能换个思路,不要由各模块主动注册,而是ModuleName主动去查找呢?实际上是可行的。我们看下具体如何实现:

  • 在bottomlibrary中提供一个接口:

public interface IModuleName {    /**     * 获取所属module的名称     * @return module的名称     */    String getModuleName();}

这里主要是为了统一服务提供形式

  • 各模块实现这个接口:
    比如在businessone模块中实现如下:

public class OneModuleName implements IModuleName {    @Override    public String getModuleName() {        return "businessone";    }}

在businesstwo模块实现如下:

public class TwoModuleName implements IModuleName {

    @Override    public String getModuleName() {        return "businesstwo";    }}
  • 各模块暴露自己的服务
    在businessone的AndroidManifest.xml中定义一个meta-data:

"com.jianglei.businessone.OneModuleName"            android:value="module_name"/>

在businesstwo模块中:

"com.jianglei.businesstwo.TwoModuleName"            android:value="module_name"/>
  • 发现服务
    所谓发现服务就是我们要在调用ModuleName.getModuleName()之前先查找各模块的具体实现服务,核心就是读取AndroidManifest.xml中定义的meta-data中的数据:

public static List getModuleName(Context context,String metaDataValue){        if(moduleName.size != 0){            return moduleName;        }        List res = new ArrayList<>();if (context == null || metaDataValue == null) {throw new IllegalArgumentException("Context or metaDataValue can not be null");        }        Bundle metaData = getMetaData(context);if (metaData == null) {return res;        }        Set keySet = metaData.keySet();        List services = new ArrayList<>();for (String metaDataKey : keySet) {            Object obj = metaData.get(metaDataKey);if (!(obj instanceof String)) {continue;            }            String registerValue = metaData.getString(metaDataKey);if(registerValue == null || !registerValue.equals(metaDataValue)){continue;            }try {                Class serviceCls = Class.forName(metaDataKey);                Object service = serviceCls.newInstance();                services.add(service);            } catch (ClassNotFoundException e) {                e.printStackTrace();            } catch (IllegalAccessException e) {                e.printStackTrace();            } catch (InstantiationException e) {                e.printStackTrace();throw new IllegalArgumentException("The service should have a public and non-parameter constructor");            }        }if (services.size() != 0) {for(IModuleName iModuleName : services){                res.add(iModuleName.getModuleName());            }        }return res;    }

我们看下现在的getModuleName的实现,核心就是读取注册在AndroidManifest.xml中的IModuleName服务,然后通过反射实例化,这样我们就成功的拿到了数据。

对比方案二,方案三无需各模块主动调用注册,而是换成了在AndroidManifest.xml中注册的形式,更加解耦,我提供什么服务就注册什么服务,这套思想其实借鉴了java的SPI机制,有兴趣的可以自行了解。

类库推荐

利用上面的思想,我封装了一个工具库,专门用来提供服务发现:
Android服务发现库https://github.com/FamliarMan/AndroidServiceProvider
大家有兴趣可以使用看看,感觉还是能省不少功夫的,如果有问题欢迎提issue。

推荐阅读

记 Android 的一次面试(初级中级水平)

在安卓的道路上铿锵前行

逆风起笔,最能得势

android module中获取 app_Android模块化中的服务发现机制相关推荐

  1. java获取jsp页面参数_jsp页面中获取servlet请求中的参数方法总结

    jsp页面中获取servlet请求中的参数的办法详解 在JAVA WEB应用中,如何获取servlet请求中的参数,并传递给跳转的JSP页面?例如访问http://localhost:8088/bbs ...

  2. 在swt中获取jar包中的文件 uri is not hierarchical

    uri is not hierarchical 学习了:http://blog.csdn.net/zdsdiablo/article/details/1519719 在swt中获取jar包中的文件: ...

  3. java List最大_在java中获取List集合中最大的日期时间操作

    取list集合中最大的日期, 可以用date max = collections.max(datelist);, 传入一个日期集合, 就可以获取, 工作中有这个需求, 就查找到这个, 代码如下 } e ...

  4. 【SpringBoot】在普通类中获取spring容器中的bean

    这段时间公司搞封闭开发,做一个联通总部的客服系统项目,是基于springboot的.在开发工程中遇到一个页面datagrid数据排序的功能,因为有多个表的数据都要用到排序功能,于是我就写了一个排序功能 ...

  5. 在普通类中获取spring容器中的bean

    在普通类中获取spring容器中的bean 1.工具类 package com.itheima.hchat.util;import org.springframework.beans.BeansExc ...

  6. vue组件获取props_vue中子组件的methods中获取到props中的值方法

    父子组件通信 这个官网很清楚,也很简单,父组件中使用v-bind绑定传送,子组件使用props接收即可 例如: 父组件中 数据统计 统计: 销售数量 {{number}} 销售金额 {{amount} ...

  7. 鸿蒙开发-在JS中获取hml页面中Input输入的值

    场景 鸿蒙基于JS搭建HelloWorld并修改国际化文件: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/118274050 在 ...

  8. 在servlet中或者在filter中获取spring容器中的bean

    1 配置spring文件 <bean id="hibernateTemplate" class="org.springframework.orm.hibernate ...

  9. vue中获取/操作组件中的dom元素

    最近刚做了一个项目,需要用到地图,选择的是腾讯地图,创建地图的时候,需要给地图创建函数中传入地图容器的id或者容器的dom元素,但是在调试过程中,发现怎么都无法获取dom元素,直接通过getEleme ...

最新文章

  1. 笔记-信息系统安全管理-网络安全防御
  2. oracle导入java包时出错,Oracle导入导出的常见错误
  3. js bind 传参、_js中的面向对象(一)
  4. 红黑树在java中的作用_带你真正理解Java数据结构中的红黑树
  5. 默认select选中其中一个option的值
  6. HTML5定稿了,终于有一种编程语言开发的程序可以在Android和IOS两种设备上运行了...
  7. Python基础篇1
  8. 威廉玛丽学院计算机教授刘旭,专栏-中国计算机学会
  9. opencv去除图片黑边,黑色背景
  10. C语言计算日期间隔天数的经典算法解析
  11. 从浏览器输入URL到页面显示的过程
  12. 八字易经算法之用JAVA实现排八字神煞
  13. Android App内截屏监控及涂鸦功能实现
  14. 怎么开发一个小程序?从零开始手把手教你
  15. Android-Socket传输 GPRS网络
  16. 玉龙雪山景区完善视频监控设施打造智慧景区
  17. java channel源码_5. 彤哥说netty系列之Java NIO核心组件之Channel
  18. mysql 查看系统参数_查看MYSQL系统参数
  19. Vivado IP核之复数浮点数累加 Floating-point
  20. 【成为博客专家】大数据面试题

热门文章

  1. html5 函数大全,5 个强大的HTML5 API 函数推荐
  2. Android开发常用命令
  3. lan交换和无线教师手册_无线AP组网方式有多种,办公楼的无线覆盖系统的组网方式有哪些?...
  4. 【java基础知识】spring框架开发时,怎样解决mysql数据库中Timestamp到String的简单转换
  5. Spark面对OOM问题的解决方法及优化总结
  6. 安卓布局,GridLayout
  7. pearson相关系数_使用gbdt我们到底应该怎么用相关系数?
  8. oracle怎么定时执行存储过程6,Oracle中如何定时调用存储过程
  9. java控制并发数量_Java并发编程中级篇(二):使用Semaphore信号量进行多个资源并发控制...
  10. 在linux centos 7上安装maven