Android组件化

Android组件化

本文由5个模块组成:

  • 1.组件化与模块化区别
  • 2.组件化家构图
  • 3.各组件之间的跳转
  • 4.各组件之间的通讯
  • 5.组件化优点
  • 6.Android Studio上面实现组件化

  • 1.组件化与模块化区别

    组件化就是基于可重用的目的,将一个大的软件系统按照分离关注点的形式,拆分成多个独立的组件,已较少耦合。
    
  • 2.组件化家构图

    Android组件化_第1张图片
    说明:Library是各组件需要依赖的一些公用库,Router(负责各组件之间的跳转)等

  • 3.各组件之间的跳转
    Router
    路由的使用范围:
    1.假设客户端与服务器之间已经有一套路由规则,比如运营人员配置一个活动,需要从这个活动跳转到不同的页面去,只需要在后台配置一下,就可以进行相对应的跳转。
    2.随着APP业务线越來越庞大,便于梳理各个业务之间的区别,便于维护,各个业务之间的接偶是必然的。因此在这个时候路由的功能就能够体现出来的。简单来说,所有的页面跳转通过Action方式来进行跳转,其实就是Android提供的一个路由结构。
    3.假设存在路由,所有页面跳转都是通过路由的方式,那么完全可以在服务器上面进行配置,一旦线上某个功能出现异常,可以在跳转的时候,路由对该功能进行拦截。
    4.当然router不仅仅只是提供跳转页面的功能,当制定好一套规则以后,也可以满足个组件之间的互相依赖需求。

    网络上面的路由框架很多,在此我就不多做介绍了。例如ActivityRouter(https://github.com/mzule/ActivityRouter.git)

  • 4.各组件之间的通讯
    先举个例子做一个APP:
    刚开始做的时候,大家完全是Activity, View, Adapter, Fragmen等相同类型的放在同一个地方,后来人来人往,新人接手后要修复一个BUG,要找半天,才可以找到所属的地方,于是便衍生了以业务为隔离的分包模式,如下:
    Android组件化_第2张图片

    此时可以看见,项目结构是十分清晰明显的,需要修改某个功能的BUG时候直接方便的多,但是后来业务需求迭代,往往一个业务需要依赖于另外一个业务,于是就变成了接下来的这样:
    Android组件化_第3张图片
    这样依赖以后,我想大家也能够想象这种会造成的问题。光是看着都觉得头疼。各个业务之间耦合很严重,修改起来简单的工作都会因为这个变得复杂化,修改一处地方,只需要几分钟,但是判断修改该处会不会影响到其他业务可能需要花好几个小时。

随着业务拓展,需要做出一些外放SDK 供第三方接入,而SDK中一些一些业务与原业务中功能一样,而原业务中可能存在各种耦合,这个时候需要提取出来所花的时间不亚于重新将该功能重写一遍了。浪费时间,浪费人力。人力不能有效发挥其功能。

那么怎么去解决这个问题呢,在下面有3个解决方案:
Android EventBus。需要什么就监听什么,但是需要相对应的模块做配合。
2.所有组件都对外暴露出一个接口,统一由一个代理托管。
3.Android 底层Binder通讯机制。(该机制属于系统架构级别的,用到一个APP应用中有些杀鸡用牛刀的感觉,哈哈)。
在这重点说一下第二个:
Android组件化_第4张图片
首先可以看到在ModuleProviderManager中保存了各个组件对外暴露的实例,其次各个组件相互并不知道其他组件的实例存在,只知道某个实例所暴露出来的接口。当某个组件需要调用另外一个组件中的功能时候,只需要从ModuleProviderManager中获取该组件的实例,只不过从ModuleProviderManager返回是对象,需要转化为相应的接口类型。

Android组件化_第5张图片
首先在Core包中声明该组件的Interface,并对外暴露出来。
然后每个组件分别实现在Core中声明的Interface。
在这个方案中,唯一的技术难点在于怎么样让各组件的Service注册到ModuleProviderManager中去。为何这么说呢,其一是因为各组件依赖于Core,也就是说Core并不知道各个组件的存在的,因此谁来负责把各组件的Service注册ModuleProviderManager需要很好的考量。(在组件可以动态化注册的时候)显然各组件不可能自己去进行注册,因此需要一个第三方,但无论是哪个第三方,都需要对各个组件进行依赖。
目前想得是这么一种方案,说的部队还望各路大神纠正:

public class ModuleProviderManager {

    public static final String  NEW_HOUSE_SERVICE = "com.baronzhang.android.newhouse.NewHouseService";
    public static final String  SECOND_HOUSE_SERVICE = "com.baronzhang.android.secondhouse.SecondHouseService";

    private ArrayList mServiceName = new ArrayList<>();


    private HashMap mService = new HashMap<>();

    private ModuleProviderManager() {
        mServiceName.add(NEW_HOUSE_SERVICE);
        mServiceName.add(SECOND_HOUSE_SERVICE);
    }

    private static class ModuleServiceManagerHolder {
        public static ModuleProviderManager sInstance = new ModuleProviderManager();
    }

    public static ModuleProviderManager getInstance() {
        return ModuleServiceManagerHolder.sInstance;
    }

    private void registerModuleProvider(Object object) {
        try {

            String serviceName = String.valueOf(object.getClass().getField("SERVICE_NAME").get(null));
            if (mService.containsKey(serviceName)) {
                return;
            }
            mService.put(serviceName, object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getModuleProviderByName(String serviceName) {
        return mService.get(serviceName);
    }

    public void initService(){
        for (String serviceName : mServiceName){
            try {
                Class clazz = Class.forName(serviceName);
                if (null == clazz) {
                    continue;
                }
                Method method  = clazz.getMethod("getInstance");
                Object obj =method.invoke(null);
                registerModuleProvider(obj);
            }catch (Exception e){
                e.printStackTrace();
                continue;
            }
        }
    }

每一个组件都需要在ModuleProviderManager中进行完整目录声明。这样只需要对该组件进行初始化即可遍历所有声明的组件,并将Service进行注册。

  • 5.组件化优点
  • 优点
    1.每个组件都有自己独立的版本,可以独立的编译,测试,打包和部署。
    2.产品组件化后能够实现完整意义上的按需求进行产品配置和销售,用户可以选择使用那些组件,组件之间可以灵活的组建。
    3.配置管理,开发,测试,打包,发布完全控制到组建层面,并带来很多好处.比如一个组件小版本进行升级,如果对外提供的接口没有发生任何变化,其他组件完全不需要再进行测试.
    4.编译,由于现有情况,因为只有修改某一处,而其他地方并未修改,在现有架构上,则需要将项目全部重新编译一次。而组件化以后,只需要单独将该组件进行编译即可。尤其当项目过于庞大的时候,整个项目全部编译一次需要的时间很长的时候,节省下来的时间就很可观。
  • 缺点
    1.颗粒度无法掌握。

    2.若是各业务之间除了跳转的联系,还有一些业务上面的耦合,业务上的如何解耦,是一个难题。

    4.若是Activity+Fragment组合方式,则其之间进行通讯起来有些困难。解决方式为写一个通用的Fragment抽象基类,里面填充一些方法供与外面Activity进行交互,所有Fragment都继承该类。通过Activity往Frangmen中注册回调接口,供Fragment通知Acitivity。但是接口总是会有局限性的。难点在于设计一套供Activity与Fragment交互的模式,其一可以方便以后的业务拓展,其二不会加强Fragment与Activity之间的耦合,其三不会造成代码泛滥,组织架构混乱。

    5.一般而言,在项目中需要把所有的Activity都声明在Manifest中。若是需要通过动态打包的时候,有些生成的Manifest中含有多余的Activity声明。设想解决方案1.只有一个Activity,所有View都用Fragment实现,通过Fragment与Fragment之间的跳转解决。设想解决方案2:只有一个Activity,但是该Activity可以在Activity栈中存在多个,分别引用不用的View。设想解决方案3:通过掉本形式动态生成Manifest,里面只包含启用的组件(该方法不同于前两种,前两种可以通过服务器方式动态开启,或者关闭,而这种不行,假设打包的时候组件A没有打包进入,则生成的Manifest中就没有A,那么想从服务器启动组件A,不太可能)。

    6.一旦采用组件方式,那么各个组件之间是没有关联的,各个组件之间是不知道对方是否存在的,因此跳转的时候需要写一个路由来进行相对应的正确跳转。再次如何写一个便于使用,且便于扩展的路由就显得十分重要。

  • 6.Android Studio上面实现组件化
    在Android Studio中,每一个组件以Library形式存在同也可以以Application形式存在,并且在每个组件下面都有2个Manifest文件,只需要每一个组件的Gradle文件中如此写到
if (isBuildModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

  sourceSets {
        main {
            if (isBuildModule.toBoolean()) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
            }
        }
    }
在全局gradle.properties文件中对isBuildModule进行赋值。


可能存在的问题:
1.文件资源冲突–可以在组件Gradle文件中对该组件资源文件名称进行前缀约束resourcePrefix “module1_XXXX”
2.重复依赖–如果是通过 aar 依赖, gradle 会自动帮我们找出新版本,而抛弃老版本的重复依赖。如果是以 project 的方式依赖,则在打包的时候会出现重复类。对于这种情况我们可以在 build.gradle 中将 compile 改为 provided,只在最终的项目中 compile 对应的 library ;

本文参考文章:

https://zhuanlan.zhihu.com/p/26744821(Android 模块化探索与实践)
https://zhuanlan.zhihu.com/p/25420181 (安居客 Android 项目架构演进)

你可能感兴趣的:(Android)