设为首页 加入收藏

TOP

Android彻底组件化方案实践(二)
2017-10-13 10:37:08 】 浏览:552
Tags:Android 彻底 组件 方案 实践
ourcePrefix "readerbook_" sourceSets { main { if (isRunAlone.toBoolean()) { manifest.srcFile 'src/main/runalone/AndroidManifest.xml' java.srcDirs = ['src/main/java','src/main/runalone/java'] res.srcDirs = ['src/main/res','src/main/runalone/res'] } else { manifest.srcFile 'src/main/AndroidManifest.xml' } } }

  通过这些额外的代码,我们给组件搭建了一个测试Host,从而让组件的代码运行在其中,所以我们可以再优化一下我们上面的框架图。
支持单独调试的组件化

2-3 组件的数据传输

  上面我们讲到,主项目和组件、组件与组件之间不能直接使用类的相互引用来进行数据交互。那么如何做到这个隔离呢?在这里我们采用接口+实现的结构。每个组件声明自己提供的服务Service,这些Service都是一些抽象类或者接口,组件负责将这些Service实现并注册到一个统一的路由Router中去。如果要使用某个组件的功能,只需要向Router请求这个Service的实现,具体的实现细节我们全然不关心,只要能返回我们需要的结果就可以了。这与Binder的C/S架构很相像。
  因为我们组件之间的数据传递都是基于接口编程的,接口和实现是完全分离的,所以组件之间就可以做到解耦,我们可以对组件进行替换、删除等动态管理。这里面有几个小问题需要明确:
● 组件怎么暴露自己提供的服务呢?在项目中我们简单起见,专门建立了一个componentservice的依赖库,里面定义了每个组件向外提供的service和一些公共model。将所有组件的service整合在一起,是为了在拆分初期操作更为简单,后面需要改为自动化的方式来生成。这个依赖库需要严格遵循开闭原则,以避免出现版本兼容等问题。
● service的具体实现是由所属组件注册到Router中的,那么是在什么时间注册的呢?这个就涉及到组件的加载等生命周期,我们在后面专门介绍。
● 一个很容易犯的小错误就是通过持久化的方式来传递数据,例如file、sharedpreference等方式,这个是需要避免的。
  下面就是加上数据传输功能之后的架构图:
组件之间的数据传输

2-4 组件之间的UI跳转

  可以说UI的跳转也是组件提供的一种特殊的服务,可以归属到上面的数据传递中去。不过一般UI的跳转我们会单独处理,一般通过短链的方式来跳转到具体的Activity。每个组件可以注册自己所能处理的短链的schme和host,并定义传输数据的格式。然后注册到统一的UIRouter中,UIRouter通过schme和host的匹配关系负责分发路由。
  UI跳转部分的具体实现是通过在每个Activity上添加注解,然后通过apt形成具体的逻辑代码。这个也是目前Android中UI路由的主流实现方式。

2-5 组件的生命周期

  由于我们要动态的管理组件,所以给每个组件添加几个生命周期状态:加载、卸载和降维。为此我们给每个组件增加一个ApplicationLike类,里面定义了onCreate和onStop两个生命周期函数。
  1. 加载:上面讲了,每个组件负责将自己的服务实现注册到Router中,其具体的实现代码就写在onCreate方法中。那么主项目调用这个onCreate方法就称之为组件的加载,因为一旦onCreate方法执行完,组件就把自己的服务注册到Router里面去了,其他组件就可以直接使用这个服务了。
  2. 卸载:卸载与加载基本一致,所不同的就是调用ApplicationLike的onStop方法,在这个方法中每个组件将自己的服务实现从Router中取消注册。不过这种使用场景可能比较少,一般适用于一些只用一次的组件。
  3. 降维:降维使用的场景更为少见,比如一个组件出现了问题,我们想把这个组件从本地实现改为一个wap页。降维一般需要后台配置才生效,可以在onCreate对线上配置进行检查,如果需要降维,则把所有的UI跳转到配置的wap页上面去。
      一个小的细节是,主项目负责加载组件,由于主项目和组件之间是隔离的,那么主项目如何调用组件ApplicationLike的生命周期方法呢,目前我们采用的是基于编译期字节码插入的方式,扫描所有的ApplicationLike类(其有一个共同的父类),然后通过javassisit在主项目的onCreate中插入调用ApplicationLike.onCreate的代码。
      我们再优化一下组件化的架构图:
    组件的生命周期.png

    2-6 集成调试

      每个组件单独调试通过并不意味着集成在一起没有问题,因此在开发后期我们需要把几个组件机集成到一个app里面去验证。由于我们上面的机制保证了组件之间的隔离,所以我们可以任意选择几个组件参与集成。这种按需索取的加载机制可以保证在集成调试中有很大的灵活性,并且可以加大的加快编译速度。
      我们的做法是这样的,每个组件开发完成之后,发布一个relaese的aar到一个公共仓库,一般是本地的maven库。然后主项目通过参数配置要集成的组件就可以了。所以我们再稍微改动一下组件与主项目之间的连接线,形成的最终组件化架构图如下:
    最终结构图.png

    2-7 代码隔离

      此时在回顾我们在刚开始拆分组件化是提出的三个问题,应该说都找到了解决方式,但是还有一个隐患没有解决,那就是我们可以使用compile project(xxx:reader.aar)来引入组件吗?虽然我们在数据传输章节使用了接口+实现的架构,组件之间必须针对接口编程,但是一旦我们引入了reader.aar,那我们就完全可以直接使用到其中的实现类啊,这样我们针对接口编程的规范就成了一纸空文。千里之堤毁于蚁穴,只要有代码(不论是有意还是无意)是这么做了,我们前面的工作就白费了。
      我们希望只在assembleDebug或者assembleRelease的时候把aar引入进来,而在开发阶段,所有组件都是看不到的,这样就从根本上杜绝了引用实现类的问题。我们把这个问题交给gradle来解决,我们创建一个gradle插件,然后每个组件都apply这个插件,插件的配置代码也比较简单:

    //根据配置添加各种组件依赖,并且自动化生成组件加载代码
     if (project.android instanceof AppExtension) {
            AssembleTask assembleTask = getTaskInfo(project.gradle.startParameter.taskNames)
            if (assembleTask.isAssemble
                    && (assembleTask.modules.contains("all") || assembleTask.modules.contains(module))) {
              //添加组件依赖
               project.dependencies.add("compile","xxx:reader-release@aar")
              //字节码插入的部分也在这里实现
            }
    }
    
    private Assembl
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇使用FragmentManager对Fragment的.. 下一篇绑定服务

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目