初始模型
组件化目录结构:
这里需要注意一下,main组件的gradle文件中,apply plugin使用的是com.android.application
其他业务模块,使用的是com.android.library
因为在最终的发布版中,其他业务组件是集成到main组件中的,main组件编译生成最终的application,或者可以这么理解,main组件和app外壳是我们app的必备组成部分,一起构成了可对外发布的完整app,其他组件可以以集成进来,也可以不集成进来,只会增加或者减少我们app的功能,但不影响我们app的最终发布。所以我们在新增module的时候,选择的类型应该是library。
各个组件都建立完成之后,接下来可以把组件集成到main组件中,集成非常简单,只需在main组件的gradle文件中,添加如下语句
各组件单独开发
前面我们把各个组件集成到到main组件,现在我们把组件拆出来,单独开发,开发完后,可以再把组件集成到main中,发布。这是组件化开发的最大亮点(优点)。
这里我们把home组件单独出来开发。第一步,需要把其library模式改为application模式,因为只有application才可以单独运行,library必须依靠application才能运行。所以,接下来,我们就要在home所对应的gradle文件中做修改。
修改成
等要集成到main组件时,又得改回来,如果这样子手工去改,组件一多,修改起来麻烦,也不优雅。优雅的解决办法就是,设置一个开关,打开时,就是application模式,可以单独开发,关闭时,就是library模式,可以集成到main组件中。
在项目根目录下,有一个gradle.properties文件
在这文件中,我们可以添加一个常量isDebug,值设为true。
这里设置了常量之后,我们项目中的其他build.gradle文件都可以把这个常量读取出来,所以我们可以在home的build.gradle文件中,读取该常量的值,动态设置applyplugin。
这样子设置之后,当我们需要切换模式时,只需要修改gradle.properties文件中isDebug的值,修改完成之后,点击Project sync按钮同步一下,如果有报错,那么还有个地方需要修改一下,就是main组件的build.gradle文件,看下图
为什么做这样的修改?因为我们把module的模式改成了application了,这里不能引入application,引入的话会报错,所以当是debug模式时(也就是组件单独开发时),这里就不引入该组件,免得报错。
接下来,还得修改 AndroidManifest.xml。因为当我们把一个module设置为application时,其AndroidManifest.xml需要包含一个app所需要的属性,例如app的icon、theme、launch Activity这些属性设置,而当module为library时,上面所提的这些属性都不需要用到,所以当我们处于不同模式时,AndroidManifest.xml文件也得不同。我们在home的src文件夹中新创建一个目录为debug目录,再把我们用于debug时的AndroidManifest.xml文件放进去。由于在Android目录模式下比较难创建这个目录,所以我们可以选另外一种目录模式(Project目录模式),然后再创建debug目录(很绕,直接看下图就会明白了)。
AndroidManifest.xml文件内容可以这样写
package="com.zhuang.home"> 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">
以上内容会有很多错误提示,其实提示的无非就是资源找不到,我们只需要把这些资源给加上就好了。可以把main组件中相应的资源拷贝过来,放在对应的目录下就可以了。
接下来在home组件的build.gradle文件中,指定不同模式下的AndroidManifest.xml文件。
sourceSets {
main {
if (isDebug.toBoolean()) {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
以上设置完成,并且sync project之后,home组件会是这样的目录
并且android studio的运行app里面,多了一个可运行的app,就是home。
由于我们home组件集成到main组件中时,是以一个fragment界面集成上去的,所以我们的home组件中,没有一个MainActivity可以作为LaunchActivity,我们可以创建一个,然后把fragment在MainActivity的layout xml文件中放进去就可以了。
所对应的activity_main.xml布局文件如下
android:name="com.zhuang.home.HomeMainFragment" android:layout_width="match_parent" android:layout_height="match_parent">
以上步骤完成之后,home就可以单独作为一个app来开发了,我们可以直接运行该组件。
共用资源的引用
实际项目中,总有一些资源需要共用,例如第三方库、自定义的工具类、自定义的view等等,甚至我们的theme。那么这些资源在“组件化”中,应该如何处理?每个组件都各自引入?
如果每个组件都各自引入这些共用资源,一方面效率差,如果一个类是所有组件都用到的,那么就得每个组件中都新增这个类,一旦这个类修改了,还得一个组件一个组件的来修改这个类;另一方面,第三方类库也有可能会冲突,例如不同组件引用了相同的第三方库,但是版本不一样,就冲突了。所以,我们得用另外的方式来处理。
解决的办法就是,也把共用库组件化,然后其他组件引用它,所以我们的架构可以演化为这样:
在项目里面把这个共用库加上
然后可以把其他第三方库、自定义view、工具类、公用资源放进该组件。例如网络请求框架retrofit,在common的build.gradle文件中:
然后其他组件,都分别引用common组件,在其他组件对应的build.gradle文件中
这样就可以使用common组件的资源了。
其实我们还可以更简化,每一个组件的build.gradle所引入的资源,其实有很多是重复的,我们一样可以把这些一并加到common组件中去,例如下面这一段,就是所有组件都有的
所以这一部分,也可以放到common里面去。所以三大业务组件里面的build.gradle文件就变成这样:
除了第三方库,还有自定义的工具类、自定义的view等等这些,就直接在common中编写java文件就行啊,其他组件能够引入这些类的。
还有就是图片、xml这些(value目录下的各种xml文件),也是可以一样被其他组件引用,这里需要特别注意的是style.xml文件,对于全局共用的style,我们应该把它也放在common中。例如我们的项目theme,本来是放在main组件的style里面,我们可以把它移到common中,这样其他组件调试时,作为一个单独的项目,也能和主项目有一样的主题。
总之就是,所有你认为可以被各个组件共享的资源,都可以放在common组件中。
资源冲突
多组件集成时,其资源文件会被归档到一起,所以如果命名重复,那么就会发生冲突,导致界面混乱。为了解决这个问题,我们可以让各个组件中的资源都有一个属于自己的前缀,例如home组件中的资源,我们可以以home_开通,video组件中的资源,我们可以以video_开头,这样就防止了资源冲突。在这里gradle可以帮我们做一点事情,就是让我们在命名资源文件时,帮我们先加上前缀。例如在home组件的build.gradle文件中,加入
resourcePrefix"home_"
这样之后我们的xml文件如果没有以home_为前缀的话,就会报错。但是这个功能其实很弱,例如xml文件报错,但是我们运行的时候,依然可以运行,图片文件不已home_为前缀,也不会报错,所以,资源冲突的问题,还需要开发者自己多多注意。
组件之间的通信
组件虽然可以拆分了,但是当他们集成到main组件中,一起工作时,还是需要一定的通信,例如业务A需要调用到业务B的一个页面,甚至进行传参。但是在“组件化”模式下,业务A和业务B是完全分开的,在业务A的认知里,根本就不存在业务B,也无从直接调用。当然,如果要A与B可以直接通信,也是可以的,互相引入就可以,但是这样的话,项目耦合性太高,架构也混乱,会把“组件化”的所有优点都一一撇掉。我们应该用另外一个方式来处理。
这里需要引入一个概念----“路由”,就如我们实际访问网络一样,我们电脑发送的请求都经过路由器转发,在“组件化”中,我们也可以设置这么一个中转站,来统一处理不同组件之间的调用关系。支持我们这一思想有一些比较有名的开源项目,如ARouter、ActivityRouter,通过这些开源项目,我们可以用一个url,由其转发,帮我们调用其他组件。
ARouter使用:
参考链接:
https://mp.weixin.qq.com/s?src=11×tamp=1599552720&ver=2571&signature=msb3wX*txhuX2O57iSYbmyqaE-0QNpTBF3ZNTpEwMhwLIhj0uO9mrZt4KCT4tvT*G9bvLRDzWjkcvrVXBY7r92TlEbV2HNEYuNZRrscIzq4EtP1dvCJAgcjqruNN8Zp4&new=1
统一版本号
各个组件的build.gradle文件中,有很多版本号。
我们可以把这些版本号统一管理起来,免得每次修改都得同时修改多份build.gradle文件,也避免不同的组件使用的版本不一样,导致冲突。
在项目build.gradle文件中(项目根目录下的build.gradle文件),定义版本号常量
ext {
compileSdkVersion = 25
buildToolsVersion = "25.0.2"
minSdkVersion = 14
targetSdkVersion = 25
versionCode = 1
versionName = "1.0"
}
然后在各个组件的build.gradle文件中,做这样的修改