组件化开发流程图笔记

时间:2022-10-30 18:04:31

概要

本文只适合想要对组件化开发有个大概认知以及复习总结,新手应该看这篇文章

在多人开发的时候,最简单的使用github托管源码,大家在开发过程中,通过push和pull代码,保持项目同步,然后打包运行测试。然而这只适合小型项目开发,在大型项目中,代码和功能都非常多,这导致编译和测试异常麻烦。基于上述,我们可以使用组件化来开发项目。所谓的组件化,是指开发的时候,把项目拆分成一个父模块和多个子模块(也就是library),大家在平时开发的时候,等于单独在开发测试一个小项目,而在发布的时候,合并成一个项目。


组件化开发的步骤

组件化项目一般包含以下几个工程

  1. 公共Library:下图中的lib_common工程,用来放一些共用模块,如http请求、图片异步加、下拉刷新、消息推送等所有项目都能用到的共用模块。

  2. app壳工程:负责管理各个业务组件,和打包apk,没有具体的业务功能;

  3. Main工程,属于业务组件,指定APP启动页面、主界面。

  4. 其他业务组件:负责各个子功能,每个组件都是一个独立的app。

    组件化开发流程图笔记

组件化具体的操作步骤

  1. 创建一个静态变量:用来标识是单独项目还是模块。在android studio的gradle.properties里面,我们可以创建属于gradle编译的静态变量,并且这里的静态变量是可以被所有的项目引用到的。组件化开发流程图笔记
    我们可以通过下面这种方式来引用静态变量的值。用来区分是编译的时候还是
if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

2.组件之间的AndroidManifest.xml合并:每个组件和Main的AndroidManifest.xml文件都需要合并到壳工程里,并且由于在测试的时候,每个业务app里面的manifest.xml都设置了application和main activity,由于每个app只能有一个启动activity和application,所以我们必须要在打包的时候去除这些东西,否则就会出错。解决的办法是我们设置两套manifest.xml,合并项目的时候一套,平时开发测试的时候一套。合并项目的那套不包含application和main activity,只有activity的申明。具体的做法是在这个业务组件的build.gradle里面去设置以下代码:

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

3.全局Context的获取及组件数据初始化:由于组件化开发的时候每个业务组件在单独开发的时候都需要有单独的application进行初始化,然而在整体运行的时候不能有单独的application。所以我们一般情况下会在通用依赖库里放一个共用的application,然后去继承他,写一些单独运行的时候需要的逻辑(比如说我们业务模块是没有登录页面的,然而在真实运行的时候肯定要登录,这个时候我们就在自定义的application写上登录逻辑,在合并的时候通过编译脚本去除这个application逻辑,也就是我们上一步的替换AndroidManifest.xml方法),这样我们就能获取通用的application Context了。在实际情况下,通常自己写的application是没有什么用的(因为一个app只需要一个application,并且在启动的时候就要做一些操作,中途仅仅拿一下application context),所以我们在合并项目的时候,会去除某些代码(当然不去除代码也是可以的,只不过会多出一些不需要的类)。下面是去除代码的方式(这是针对于某个子library的,所以我们需要在这个模块中的build.grale中写这段代码):

sourceSets {
        main {
            if (isModule.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成开发模式下排除debug文件夹中的所有Java文件
                java {
                    exclude 'debug/**'
                }
            }
        }
    }

4.library依赖问题
通常情况下,我们会在业务项目里面互相依赖某些第三方library,比如说业务项目A依赖了httpclient包,业务项目B也依赖了httpclient包,在打包的时候,我们的android studio只会打包一份进来。所以不会造成重复引用问题。然而在某些情况下,这些第三方library里面又会依赖某些第三方library,因为我们android studio只会默认排除不同的library,不会排除第三方library里面的library(最典型的是v4.jar包问题,多个项目依赖的第三方library可能会都依赖了v4.jar包,而且版本不一致)。所以有时候会冲突。这个时候我们可以通过exclude函数排除某个compile。具体的写法是:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion") {
        exclude module: 'support-v4'//根据组件名排除
        exclude group: 'android.support.v4'//根据包名排除
    }
}

虽然通过上述方法能够解决重复依赖问题,但是这样做肯定不是非常好的,除了重复依赖,还有版本问题。所以最好的方式是我们直接在common库的dependencies 去compile这些第三方library,并且在全局的build.gradle文件里面设置版本号。具体做法如下:

首先我们在build.gradle里面写好版本值

组件化开发流程图笔记

然后我们通过$符号引用这个值

组件化开发流程图笔记

5.组件之间调用和通信。组件的通信一般有两种情况:页面跳转和消息通信(比如说我们要通知某个页面及时刷新)。前者使用匿名调用实现,后者通过eventBus或者广播来实现。
6.组件之间资源名冲突:如果两个工程中有同一个图片或者字符串名,肯定是不行的,最好的解决办法是我们在命名的时候约定好。我们也可以通过gradle来解决:

resourcePrefix "girls_"

设置了上述属性,我们xml文件下的命名必须以这个前缀开始,否则会报错。然而它只能作用于xml文件里的前后缀,并不能指定图片的命名。


组件化里面的工程类型

在组件化工程模型中主要有:app壳工程、业务组件和功能组件3种类型,而业务组件中的Main组件和功能组件中的Common组件比较特殊,下面将分别介绍。

  1. app壳工程:他的作用跟他的名字一样,用于包装所有的业务组件和common组件。它永远都是一个application工程,且他里面的AndroidManifest.xml文件里面包含了application的申明、整个应用程序的路由协议、用于处理组件跳转的 URL,最终common和组件的AndroidManifest都会合并到这个文件来。它的build.gradle里面申明了应用的图标、名字、支持的属性配置、混淆编译的方法等。
  2. Common组件包含以下几个特点

    • 功能组件的 AndroidManifest.xml 用来存放uses-permission 和 uses-feature值。所以我们别的模块不需要权限申明了。

    • 不管是在集成开发模式下还是组件开发模式下属性始终是: com.android.library,所以功能组件是不需要读取 gradle.properties 中的 isModule 值的;另外功能组件的 build.gradle 也无需设置 buildTypes .只需要 dependencies 这个功能组件需要的jar包和开源库。

    • Common组件的资源文件中需要放置项目公用的 Drawable、layout、sting、dimen、color和style 等等,另外项目中的 Activity 主题必须定义在 Common中,方便和 BaseActivity 配合保持整个Android应用的界面风格统一。

    • Common组件中封装了Android应用的 Base类和网络请求工具、图片加载工具等等,公用的 widget控件也应该放在Common 组件中;业务组件中都用到的数据也应放于Common组件中,例如保存到 SharedPreferences 和 DataBase 中的登陆数据;

3.业务组件和Main组件。业务组件和Main有以下几个特点

  • 业务组件中要有两张AndroidManifest.xml,分别对应组件开发模式和集成开发模式,这两张表的区别请查看 组件之间AndroidManifest合并问题 小节。

  • 业务组件在集成模式下是不能有自己的Application的,但在组件开发模式下又必须实现自己的Application并且要继承自Common组件的BaseApplication,并且这个Application不能被业务组件中的代码引用,因为它的功能就是为了使业务组件从BaseApplication中获取的全局Context生效,还有初始化数据之用。

  • 业务组件通常会创建一个debug包,这个包在集成模式下会从业务组件的代码中排除掉,所以debug包中的类不能被业务组件强引用,例如组件模式下的 Application 就是置于这个文件夹中,还有组件模式下开发给目标 Activity 传递参数的用的 launch Activity 也应该置于 debug 包中;

  • 业务组件必须在自己的 Java文件夹中创建业务组件声明类,以使 app壳工程 中的 应用Application能够引用,实现组件跳转,具体请查看 组件之间调用和通信 小节;

  • 业务组件必须在自己的 build.gradle 中根据 isModule 值的不同改变自己的属性,在组件模式下是:com.android.application,而在集成模式下com.android.library;同时还需要在build.gradle配置资源文件,如 指定不同开发模式下的AndroidManifest.xml文件路径,排除debug文件夹等;业务组件还必须在dependencies中依赖Common组件,并且引入ActivityRouter的注解处理器annotationProcessor,以及依赖其他用到的功能组件。

  • Main组件集成模式下的AndroidManifest.xml是跟其他业务组件不一样的,Main组件的表单中声明了我们整个Android应用的launch Activity,这就是Main组件的独特之处;所以我建议SplashActivity、登陆Activity以及主界面都应属于Main组件,也就是说Android应用启动后要调用的页面都应属于Main组件。


组件化混淆方法

我们推荐不要再各个模块中混淆,因为模块在编译的时候会以attr的方式导入主项目中。如果提前混淆,在出错的时候很难定位具体的错误。所以混淆统一在壳项目中进行。这也是不推荐在业务组件的 build.gradle 文件中配置 buildTypes (构建类型)的原因。


工程的build.gradle和gradle.properties文件

在组件化项目中因为每个组件的 build.gradle 都需要配置 compileSdkVersion、buildToolsVersion和defaultConfig 等的版本号,而且每个组件都需要用到 annotationProcessor,为了能够使组件化项目中的所有组件的 build.gradle 中的这些配置都能保持统一,并且也是为了方便修改版本号,我们统一在Android工程根目录下的build.gradle中定义这些版本号,当然为了方便管理Common组件中的第三方开源库的版本号,最好也在这里定义这些开源库的版本号,然后在各个组件的build.gradle中引用Android工程根目录下的build.gradle定义的版本号。


组件化工程的gradle.properties文件

在组件化实施流程中我们了解到gradle.properties有两个属性对我们非常有用:

1、在Android项目中的任何一个build.gradle文件中都可以把gradle.properties中的常量读取出来,不管这个build.gradle是组件的还是整个项目工程的build.gradle;

2、gradle.properties中的数据类型都是String类型,使用其他数据类型需要自行转换;

利用gradle.properties的属性不仅可以解决集成开发模式和组件开发模式的转换,而且还可以解决在多人协同开发Android项目的时候,因为开发团队成员的Android开发环境(开发环境指Android SDK和AndroidStudio)不一致而导致频繁改变线上项目的build.gradle配置。

在每个Android组件的 build.gradle 中有一个属性:buildToolsVersion,表示构建工具的版本号,这个属性值对应 AndroidSDK 中的 Android SDK Build-tools,正常情况下 build.gradle 中的 buildToolsVersion 跟你电脑中 Android SDK Build-tools 的最新版本是一致的,比如现在 Android SDK Build-tools 的最新的版本是:25.0.3,那么我的Android项目中 build.gradle 中的 buildToolsVersion 版本号也是 25.0.3,但是一旦一个Android项目是由好几个人同时开发,总会出现每个人的开发环境 Android SDK Build-tools 是都是不一样的,并不是所有人都会经常升级更新 Android SDK,而且代码是保存到线上环境的(例如使用 SVN/Git 等工具),某个开发人员提交代码后线上Android项目中 build.gradle 中的 buildToolsVersion 也会被不断地改变。

另外一个原因是因为Android工程的根目录下的 build.gradle 声明了 Android Gradle 构建工具,而这个工具也是有版本号的,而且 Gradle Build Tools 的版本号跟 AndroidStudio 版本号一致的,但是有些开发人员基本很久都不会升级自己的 AndroidStudio 版本,导致团队中每个开发人员的 Gradle Build Tools 的版本号也不一致。

如果每次同步代码后这两个工具的版本号被改变了,开发人员可以自己手动改回来,并且不要把改动工具版本号的代码提交到线上环境,这样还可以勉强继续开发;但是很多公司都会使用持续集成工具(例如Jenkins)用于持续的软件版本发布,而Android出包是需要 Android SDK Build-tools 和 Gradle Build Tools 配合的,一旦提交到线上的版本跟持续集成工具所依赖的Android环境构建工具版本号不一致就会导致Android打包失败。

为了解决上面问题就必须将Android项目中 build.gradle 中的 buildToolsVersion 和 GradleBuildTools 版本号从线上代码隔离出来,保证线上代码的 buildToolsVersion 和 Gradle Build Tools 版本号不会被人为改变。

相关文章