Android进阶-组件化架构详解(放弃模块化到全面拥抱组件化开发)

一、为何放弃模块化?

模块化开发就是将常用的UI、网络请求、数据库操作、第三方库的使用等公共部分抽离封装成基础模块,或者将大的业务上拆分为多个小的业务模块,这些业务模块又依赖于公共基础模块的开发方式。

图片

模块化的弊端

1、项目代码量越来越大,每次的编译速度越来越慢,哪怕几句代码的修改,都需要等待若干分钟等待编译运行查看执行结果,极大的降低了开发效率;

2、业务模块越来越多,不可避免地产生越来越多且复杂的耦合,哪怕一次小的功能更新,也需要对修改代码耦合的模块进行充分测试;

3、团队人数越来越多,却要求开发人员了解与之业务相关的每一个业务模块,防止出现此开发人员修改代码导致其他模块出现bug的情况,这个要求对于开发人员显然是不友好的。

那怎样解决模块化开发的这些弊端呢?

当然是组件化

二、组件化开发

1、什么组件化?为何使用?

  • 组件化就是将工程按照不同的属性拆分成各个独立的子工程的过程;组件是组件化的输出产物,不同的组件最终进行组装就是完整的工程。
  • 随着业务越来越复杂,代码量越来越盘大的时候,开发人员在开发新功能或修改bug的时候有时候会不小心将其他正常的功能修改出新bug。
  • 而组件化之后,每一个组件都是独立的互不影响。
  • 开发完成后,也能对单个组件进行单独发布,测试人员就能对该组件进行单独的测试,而不用受其他不相关的组件的影响。

2、组件化好处

2.1组件化,要更关注可复用性、更注重关注点分离、功能单一、高内聚、粒度更小、是业务上能划分的最小单元;

2.2组件,既可以作为library,又可以单独作为application,便于单独编译单独测试,大大的提高了编译和开发效率;

(业务)组件,可有自己独立的版本,业务线互不干扰,可单独编译、测试、打包、部署;

2.3各业务线共有的公共模块开发为组件,作为依赖库供各业务线调用,减少重复代码编写,减少冗余,便于维护;

2.4通过gradle配置文件,可对第三方库的引入进行统一管理,避免版本冲突,减少冗余库;

2.5通过gradle配置文件,可对各组件实现library与application间便捷切换,实现项目的按需加载。

图片

3、组件化实际开发

3.1 library和app模式的转换

Android Studio中的Module主要有两种属性,分别为:

3.1.1 application属性,可以独立运行的Android程序,也就是我们的APP;

apply plugin: ‘com.android.application’

3.1.2、library属性,不可以独立运行,一般是Android程序依赖的库文件;

apply plugin: ‘com.android.library’

①Module的属性是在每个组件的 build.gradle 文件中配置的,当我们在组件模式开发时,业务组件应处于application属性,这时的业务组件就是一个 Android App,可以独立开发和调试;而当我们转换到集成模式开发时,业务组件应该处于 library 属性,这样才能被我们的“app壳工程”所依赖,组成一个具有完整功能的APP;

②但是我们如何让组件在这两种模式之间自动转换呢?总不能每次需要转换模式的时候去每个业务组件的 Gralde 文件中去手动把 Application 改成 library 吧?如果我们的项目只有两三个组件那么这个办法肯定是可行的,手动去改一遍也用不了多久,但是在大型项目中我们可能会有十几个业务组件,再去手动改一遍必定费时费力,这时候就需要程序员发挥下懒的本质了;

③Gradle自动构建工具有一个重要属性,可以帮助我们完成这个事情。每当我们用AndroidStudio创建一个Android项目后,就会在项目的根目录中生成一个文件 gradle.properties,我们将使用这个文件的一个重要属性:在Android项目中的任何一个build.gradle文件中都可以把gradle.properties中的常量读取出来;那么我们在上面提到解决办法就有了实际行动的方法,首先我们在gradle.properties中定义一个常量值 isModule(是否是组件开发模式,true为是,false为否),

每次更改“isModule”的值后,需要点击 “Sync Project” 按钮,

isModule=false

然后我们在业务组件的build.gradle中读取 isModule,每次改变isModule的值后,都要同步项目才能生效。

if (isModule.toBoolean()) {    apply plugin: 'com.android.application'} else {    apply plugin: 'com.android.library'}

3.2组件之间AndroidManifest合并问题

组件开发模式下的业务组件再创建一个 AndroidManifest.xml,然后根据isModule指定AndroidManifest.xml的文件路径,让业务组件在集成模式和组件模式下使用不同的AndroidManifest.xml,这样表单冲突的问题就可以规避了。

这样在不同的开发模式下就会读取到不同的 AndroidManifest.xml ,然后我们需要修改这两个表单的内容以为我们不同的开发模式服务。

业务组件的 build.gradle 中指定表单的路径,代码如下:

  sourceSets {        main {            if (isModule.toBoolean()) {                manifest.srcFile 'src/main/module/AndroidManifest.xml'            } else {                manifest.srcFile 'src/main/AndroidManifest.xml'            }        }    }

3.3全局Application问题

①组件化开发的时候,可能为了数据的问题每一个组件都会自定义一个Application类,如果我们在自己的组件中开发时需要获取 全局的Context,一般都会直接获取 application 对象,但是当所有组件要打包合并在一起的时候就会出现问题,因为最后程序只有一个 Application,我们组件中自己定义的 Application 肯定是没法使用的,因此我们需要想办法再任何一个业务组件中都能获取到全局的 Context,而且这个 Context 不管是在组件开发模式还是在集成开发模式都是生效的。

②BaseApplication 主要用于各个业务组件和app壳工程中声明的 Application 类继承用的,只要各个业务组件和app壳工程中声明的Application类继承了 BaseApplication,当应用启动时 BaseApplication 就会被动实例化,这样从 BaseApplication 获取的 Context 就会生效,也就从根本上解决了我们不能直接从各个组件获取全局 Context 的问题。

③给个代码示例

 // ------------BaseApplication------------ public class BaseApplication extends MultiDexApplication {

@Override public void onCreate() { super.onCreate();

// 初始化全局配置 initGlobalConfig();

// 初始化模块配置 initModuleConfit(); }

private void initGlobalConfig() { // 初始化ARouter initARouter(); }

private void initModuleConfit() { for (String modules : ModuleConfig.MODULELIST){ try { Class clz = Class.forName(modules); Object obj = clz.newInstance(); if (obj instanceof BaseApplicationImp){ ((BaseApplicationImp) obj).onCreate(this); }

} catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } }

private void initARouter() { ARouter.openLog(); ARouter.openDebug(); ARouter.init(this); } } // ------------BaseApplicationImp------------ public interface BaseApplicationImp{ void onCreate(Application application); } // ------------ModuleConfig------------- public interface ModuleConfig {

static final String MODULE_PDFREADER = "com..BaseApplication";     static final String MODULE_LAUNCHER = "com.app.AppContext";

public static final String[] MODULELIST = { MODULE_LAUNCHER, MODULE_PDFREADER }; }

创建BaseApplication类,ModuleConfig类以及BaseApplicationImpl类。BaseApplication类的onCreate()方法中初始化一些全局配置并且初始化模块配置。BaseApplicationImpl类是一个接口类,需要各模块自己去实现各个模块的配置。这些配置的类是定义在ModuleConfig中,在初始化的时候会通过反射创建这些类。

在各个子module中实现BaseApplicationImpl,这个类可以提供模块化的配置以及application context对象。清单文件都设置为BaseApplication即可。

3.4组件之间调用和通信组件之间是平行结构的,它们之间相互没有交集,要实现通信只有添加一个中间层将它们连接到一起,这就是“路由”的概念路由就像一个桥梁一样让平行的河流(组件)之间可以通信和传递数据,这样看起来好像他们之间又是强耦合的关系,维护起来还是代价还有点大,那么还有没有好点的办法呢?那就是路由层使用接口和其他module之间建立弱耦合的关系。

通信方案有以下几种:

① 直接依赖

这种方式最直接,但是解耦不彻底,可以直接使用依赖模块的所有类。需要开发者自己规范自己的依赖。

②事件通知

通过广播或Bus广播消息,性能较低,而且代码维护成本很高

③路由

这应该是目前比较主流的方案,方便,维护成本比较低。主要用来解决启动其他模块的Activity或Fragment的问题。但是无法直接调用其他模块的方法,而且声明方一旦修改就需要通知调用方

④公用模块

创建一个公共模块,在该模块中放入各个模块需要暴露的接口。各个模块依赖中间模块,提供服务的模块内部提供实现接口供其他模块调用,公共模块的代码都可以编辑。其他模块的人修改了接口,有可能本模块也得修改。若不想引用其他模块的实现类,还需要反射,限制实现类名称等,维护成本比较低,但是限制比较多

推荐的是路由框架

目前如果不考虑实现和成本,可以直接拿来使用的是

WMRouter

美团外卖Android开源路由框架

WMRouter最初用于解决美团外卖App在业务演进过程中的实际问题,之后逐步推广到了美团其他App。

ARouter

阿里开源的一个 Android 路由中间件,用于对页面、服务提供路由功能

使用方法直接网络上搜索很多的,如果找不到直接发信息给小编。

总结

  • 组件模式下可以加快编译速度,提高开发效率;
  • 自由选择开发框架(MVC /MVP / MVVM /);
  • 方便做单元测试;
  • 代码架构更加清晰,降低项目的维护难度;
  • 适合于团队开发;
  • 后续小编会亲自写路由框架,小而美,供大家使用。
阅读原文

简介:一个有10多年经验开发的android、java、前端等语言的老程序员,在这里一起聊聊技术,一起聊聊生活、一起聊聊中年危机的生存之道,一起进步一起加油,感兴趣的欢迎订阅;不定时的更新。欢迎关注微信公众号:Android开发编程
(0)
打赏 喜欢就点个赞支持下吧 喜欢就点个赞支持下吧

声明:本文来自“Android开发编程”,分享链接:https://www.zyxiao.com/p/291046    侵权投诉

网站客服
网站客服
内容投稿 侵权处理
分享本页
返回顶部