组件化开发想必在各大厂中都已经运用得十分成熟了,但是在大部分的小公司中,并没有开始运用,一方面由于业务逻辑还没复杂到需要利用组件化开发来提高开发效率,另一方面,组件化开发有未知的风险,也让人不得不望而却步。本篇并不作为组件化开发的介绍或者教程,仅仅作为之前一段时间的学习总结。
1.组件化
当应用的业务变得复杂繁重时,编译时间就会变长,尽管有各种优秀编译工具或是配置可以让编译过程变得不那么“痛苦”,但是相应的 bug 也层出不穷。在这样的背景下,组件化开发应运而生。它的出现让单元测试成为可能。
组件化开发的思路是拆分每个业务模块,让编译只专注与当前的逻辑,而不用去管其他模块,通过减少编译无关开发功能的代码,来达到减少编译时间,提高开发效率的目的。
2.试一试
目标:让一个模块既可以被依赖,又能够独立运行
首先我们创建一个工程,然后在工程目录下创建一个 module ,module 类型暂时定为 application。在这种情况下,app 和 module 都是可以独立运行的。下面,我们需要把 module 作为组件依赖给 app 主工程。
想依赖成功的话,需要在 gradle 文件中修改如下代码:
//apply plugin: 'com.android.application'
apply plugin: 'com.android.library'
android {
...
defaultConfig {
...
//applicationId "com.arno.testcomponent"
...
}
...
}
那么按照上面的步骤,我们就可以简单实现一个 module 的组件化,如果报错的话,请检查一下是否有重复依赖第三方库,是不是看着挺简单的? 然而,这样做其实还有许多隐患,同时,在组件变多的时候,这种做法显得一点也不聪明,光是更改 apply plugin: 'com.android.library' 这一条,就够累人的了。因此,我们需要对它进行优化。
3.一点优化
3.1 module 的编译方式
开发安卓的朋友们一定都熟悉 java ,所以用 java 开发的思路无非是加一个开关,通过对开关的判断来选择当前 module 的模式。我们这里也是同样的想法,不过实现的方式是 groove。
为了把所有开关集中起来,我们在工程下新建了一个 .gradle 文件,这里我取名为 config.gradle:
ext{
flag=[
//module是否是作为模块,true的时候是,false的时候可以独立运行
isModle : true,
]
}
如果需要这个 .gradle 文件生效,还需要在工程目录下的 build.gradle 文件中加入下面的代码:
apply from: "config.gradle"
然后,我们就可以在 module 的 gradle 中获取这个值,从而判断这时需要以 lib 的方式加载,还是以 app 的方式加载。代码如下:
if(rootProject.ext.flag["isModule"]) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
如果你真的按照上面的做法做了,并把代码打包安装到手机上,那么你可能会发现,出现了非常有意思的事情:出现了两个相同的app。同时,卸载其中一个的话,另一个也会消失。
安装后的app那么,它们打开的界面是 module 中的 MainActivity, 还是 app 中的MainActivity 呢?为此,我特意稍微修改了一下 module 中 MainActivity 的布局后,再次安装,发现它们打开的都是 app 中的 MainActivity。然而,我并不知道是为什么,正在提问中。不过没关系,我知道怎样解决。
3.2 清单文件冲突
虽然做这个部分是为了避免 module 的清单文件与 app 的设置冲突,但是神奇的是,做完这个部分后,上面的 bug 消失了。
由于不同组件是由不同的成员开发的,所以 module 中的清单文件是不同的,但是当这些 module 作为 Library 合并到主工程时,两张表便会起冲突,因为他们都有自己实现 application 的一些属性,也有自己的 MainActivity 。为此,我们需要在 module 中同时维护两张表,一张用于独立的组件开发,一张用于合并到主工程,每增加一个四大组件的时候,就需要同时给两张表中添加。
优化的方法,我们参考对 module 编译方式的优化方法。
首先,我们需要两张表,为了区分,我们将它放在不同的文件夹中去,如下图:
工程目录
其中,debug 中的 AndroidManifest.xml 和创建时候一样,而 relese 中的 AndroidManifest.xml 则把大部分属性全部删除,只留下必须的部分。
//debug
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.arno.module">
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"
</intent-filter>
</activity>
</application>
</manifest>
//release
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.arno.module">
<application android:theme="@style/AppTheme">
<activity android:name=".MainActivity"></activity>
</application>
</manifest>
之后需要在 module 的 build.gradle 中配置 AndroidManifest.xml 文件的路径。
android {
...
//区分加载 AndroidManifest 文件
sourceSets{
main{
if (rootProject.ext.flag["isModule"].toBoolean()){
manifest.srcFile 'src/main/release/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
}
}
}
}
3.3 一些数据文件共享和sdk初始化
在组件化过程中会发现这样的问题:一个app只能配置一个 Application 子类,而在开发过程中难免要用到自定义 Application,这个时候,如果开发每个组件在合并过程中必然出现问题。因此,我们会有一个 common 库,所有组件都必须依赖这个库,而这个库中将放置所有公共的代码,如sdk初始化,一些工具类,依赖库等等。
3.4 第三方库版本号控制
实际开发中会遇到许多第三方库的依赖,有时我们需要升级一些库,但是这个过程有时会遇到错误,导致长时间的问题排查。因此我们可以将库的版本管理集中到一起。
这里我们将版本信息全部集中到之前自定义的 config.gradle 中去。
// config.gradle
ext{
...
android=[
compileSdkVersion : 25,
buildToolsVersion : "26.0.1",
minSdkVersion : 21,
targetSdkVersion : 25,
]
dependencies = [
constraint : 'com.android.support.constraint:constraint-layout:1.0.2',
appcompatv7 : 'com.android.support:appcompat-v7:25.3.1',
]
}
// common build.gradle
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
...
}
}
dependencies {
...
compile rootProject.ext.dependencies["appcompatv7"]
compile rootProject.ext.dependencies["constraint"]
}
以上是本次学习的全部内容,关于组件化学习的组件之间的通信可以通过 EventBus ,这个比较容易,也就不再去详细写了,接下来还在学习 ARouter 路由框架的使用和实现,后面会更新。还要努力。
网友评论