组件&插件化之复习

 一. 插件化

为什么有插件化?
有了组件化,为什么还要用插件化呢?插件化开发总的来说有以下几点好处(不同插件框架不一样):

宿主和插件分开编译
并发开发
动态更新插件
按需下载模块
方法数或变量数爆棚
处境
开放出来的插件化开发框架比较多,他们各自都有自己的优势和和不足,实现的原理也有差别下面列

开源的插件化框架

Qihoo360/DroidPlugin
CtripMobile/DynamicAPK
mmin18/AndroidDynamicLoader
singwhatiwanna/dynamic-load-apk
houkx/android-pluginmgr
bunnyblue/ACDD
wequick/Small
……

目前开源的这几个框架有宿主和插件分离的也有融合在一起的,每个框架的详细介绍和demo在github里都可以查看到。插件化demo运行起来比较简单,但是真正将它用到实际项目中还是要考虑很多小细节的,目前我也正处于研究阶段。

二.组件化

Android组件化探索与实践
随着业务复杂性不断的增加,项目的代码量会不断的增大,这对于项目的维护提出了更大的挑战。Android的组件化开发就是旨在解决大型项目的可维护性、可扩展性的难题。
一、组件化简介
组件化是大型Android项目的一个好的解决方案。通过对项目工程进行组件化,利于代码的维护与扩展,特别是在多团队协作开发的模式中,尤其有效。

对于Android组件化的讨论其实已经持续很长的一段时间了,也产生了很多种的组件化方案,不同的组件化方案有不同的利弊,但是其组件化目标基本一致,只是实现思路可能不同。实现的一种组件化方案。

首先需要了解什么是组件化?简单来说,组件化就是对项目依据业务功能拆分成不同的模块,这些业务功能模块彼此独立,不相互依赖,既可以独立编译运行,也可一起打包成一个app。通过下图来对比一下组件化的特点。

图1.1传统项目组织方式

 

图1.2 Android组件化方式

通过上图可以明显的看出,传统的项目组织形式,不同模块之间会相互引用,造成耦合。而在组件化工程中,不同业务模块之间是没有相互引用关系的,彼此项目独立。

二、组件化目标
在了解了组件化的定义之后,需要明确组件化的目标主要是什么。组件化的目标主要是解决如下几方面的问题。

解耦合
随着项目不断的迭代,代码越来越庞大、臃肿,这时代码的维护就会显得极其繁重。如果能够根据业务来对项目进行业务模块划分,不同开发人员来维护不同业务模块,同时业务模块之间彼此无关联,这样就方便了项目的维护。

代码复用
组件化后,可以将一些功能组件、业务组件做成aar,上传到maven私服,这样便于其他团队通过maven来引用,快速的引入相关的功能模块。

加快编译速度、偏于开发调试
当项目很大的时候,编译一次是非常耗时的。如果项目采用组件化,那么业务模块可以作为主module运行,这样在开发阶段,可以只加载开发人员关心的模块,而无需加载所有模块,这样会大大的提供编译速度,同时也可以明确问题边界,利于bug的分析、处理。

便于多团队协同开发、维护
当不同团队维护一个app时,是极容易造成代码冲突的。组件化即可完美解决这个问题,不同的业务团队维护不同的业务组件,业务组件之间彼此隔离,不会相互干扰。

三、组件化设计
在上一节中明确组件化需要解决的问题,本小结就来讨论下如何通过组件化的设计来逐条解决上述问题。

3.1 解耦合
在传统方式中,不同的module之间想要相互调用,需要通过在gradle文件中相互引用,这样才可以获取到module中的类。而在组件化中,需要在不同模块彼此隔离的情况下相互调用,这样才能达到解耦合的目的。

组件交互的方式:

1)路由框架实现不同业务模块之间的隔离(组件之间页面的跳转通过此方式解决)。采用阿里的ARouter。通过ARouter可以实现Fragment的实例获取、Activity之间的跳转、交互等。

业务组件B想要跳转到业务组件A中的Tab页面,可以通过路由框架跳转:
业务组件A中TabActivity定义如下:

@Route(path = "/app/tab")
public class TabActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

业务组件B中利用路由框架来跳转:

ARouter.getInstance().build("/app/tab").navigation()  

可以看到,通过路由框架,在业务组件B中,无需引入业务组件A中的TabActivity类即可实现页面的跳转。这样就达到了解耦合。

组件之间页面的跳转可以通过如上的方式来解决。

2)还有一种情景,通过如上的路由方式是解决不了的,即接口的调用:接口下沉方式。

例如:业务组件B想要调用登录组件中获取登录信息的方法。想要实现这样的功能,比较通用的实现方式是接口下沉。

图3.1接口下沉方式

这种方式缺点:每个业务组件都需要把对外暴露的方法下沉到底部的组件中,这样其实并没有很好的实现解耦合。

3)而我们组件化方案采用一种不同方式,本文论述的组件化核心内容。采取的方案是将业务组件拆分成两个模块:对外暴露的接口模块、业务功能模块。

图3.2 模块拆分

以上图为例,将原本的业务模块Module A,拆分成接口模块Module A interface、业务模块Module A。接口模块的作用是对外暴露接口,而业务模块是接口真正的实现类,其会实现Module A interface中的接口。若其他模块想要调用此模块的相关功能,则只需引用其接口模块,而无需引用其业务模块,这样就实现了业务模块之间的解耦合。

剩下要解决问题是如何在只引用接口的情况下,就能调用接口实现类的相关方法。采用的方式是编译期注解处理、反射等技术。

首先,需要定义两个注解,BxBundleService、BxService。BxService的作用是标记对外暴露接口的实现类,BxBundleService的作用是标记需要注入的接口实例。以登录模块为例示例如下:

登录接口模块对外提供的接口:

登录业务模块中会实现上述的接口,通过BxService注解来标记其为实现类:

其他业务模块可以在需要的时候调用登录模块中的方法:

主要作用就是找到LoginService的全路径名,并通过反射生成其实例。

其次,有了注解的标记,接下来就是注解的处理,为了提高程序的运行效率,没有采用运行时处理注解,而是采用了编译期间处理注解,具体如下:

1.引入编译期注解处理类库,在gradle文件中引入:

 2. 定义Processsor,在编译期对注解进行处理

注解处理,这点也是本组件化实践过程中的核心要点
3.1 遍历BxService注解标记的类,对其生成一个辅助源文件,以登录模块为例,生成如下的辅助文件

3.2 遍历含有BxBundleService注解的类,完成BxBundleService标记的属性对象的注入,其也是通过生成辅助源文件的方式来完成。生成的文件如下

辅助源文件的生成可以使用javapoet库来实现。这里不再论述。

4.流程串联,在使用登录模块时,需要通过BxbankModuleBus.inject(this)方法先完成服务的注入,其实现方式如下

通过代码可以看出,其通过反射获取到BxbankModuleBusInterface实例,并调用期bxbankModuleBusInject方法,此时再看上面3.2中的BxbankModuleBusInterface的实现类可以看到,bxbankModuleBusInject方法的主要作用就是找到LoginService的全路径名,并通过反射生成其实例,并最终将其实例注入到引用对象中。至此完成了loginService的实例注入,之后就可以通过loginService实例来进行登录模块的相关调用。

通过前面的论述可以看到模块间只需引入模块的接口模块,就可完成模块间的相互调用,达到了解耦合的目的。

3.2 代码复用
对代码进行模块拆分后可以看到,代码已经完成了高内聚、低耦合,一些基础的功能模块、业务模块可以提供给其他项目组来复用。

3.3 加快编译速度、偏于开发调试
有时全量编译整个工程是很耗时的,也是不必要的。例如某个开发者只负责登录模块,那么其可以排除其他的业务模块,而只编译、运行登录模块来进行开发和维护,这样可以大大的提供编译效率,提升开发体验。可以采用如下的技术方案来达到这个目的:

首先:在工程的build.gradle文件中定义个变量来控制组件是否可以独立编译、运行

其次:在模块组件的build.gradle文件中通过上面定义的变量来决定其是否作为主module运行

最后:需要对源文件进行处理,因为主module的manifest文件中,应包含launcher页面的入口,如下:

 

从配置中可以看出,根据isModule变量的不同,源码则引入不同的manifest文件,这样就实现了业务组件的独立编译运行。

3.4便于多团队协同开发、维护
通过之前的论述可以看出,项目进行组件化改造之后,各个业务组件之间相互独立,无直接的引用,这样不同业务组件之间可以进行独立的黑盒开发、面向接口开发,而彼此不会相互依赖,极大的方便了多团队的协同开发,减少冲突的可能行。

四、总结
前面论述了组件化的定义、目标、实现思路等内容。在进行组件化探索与实践中,主要用到的技术包括路由、编译期注解处理、反射、gradle配置等技术,通过这些技术方案的实施,形成了我们自己的组件化方案框架,达到了组件化的目的。

 

posted on 2020-05-04 00:10  左手指月  阅读(178)  评论(0编辑  收藏  举报