本文所对应的示例代码,已经上传到这里:
https://download.csdn.net/download/fenggering/10876548
现在稍微有点规模的App项目,都会采用组件化的开发方式。
组件化通俗来说就是把app分成多个部件,各个部件职责较为单一,可以单独修改调试,也可以集成到一起发挥作用,这样就能很方便进行团队协作和版本更新。
组件化开发需要解决的几个问题(点击可跳转到对应小节):
一.如何统一管理编译环境
二.如何实现各个组件既能单独调试运行,又能集成到整体里
三.如何避免组件之间资源引用
四.组件之间如何跳转,传递数据
先建好项目里的各个组件,为开发做准备:
首先,我们新建一个项目。这个项目中默认会包含一个module,名为app,我们就称之为app组件,做为项目的主组件。其他组件最终都要集成进来。
接着,为实现组件化,需要建立一个基础通用组件,我们暂且称为lib组件。这个lib组件作用是实现一些共通的接口,方便其他组件之间数据通信和跳转等。也就是说lib组件应该被其他所有组件所依赖。如何新建lib组件呢?
Android studio的File -> new -> new moudle -> Android Library,点击下一步就新建了lib组件。在lib组件的build.gradle文件第一行,可以看到它的属性是library:
apply plugin: 'com.android.library'
同时我们再新建一个组件,假设这个组件是用来实现登录相关功能的,称为login组件,而且我们想让它可以单独运行和调试。如何新建呢?跟新建lib组件类型,但最后要选的不是Android Library,而是Phone & Tablet module。
这样完成后,在login组件的build.gradle里,可以看到login组件的属性是Application,这样它就可以单独运行和调试:
apply plugin: 'com.android.application'
那么login组件和lib组件还有什么区别呢?
lib组件没有生成默认的activity,Manifest文件内容也比login组件的简单得多。而且在lib组件的Build.gradle里,没有applicationId这一项。
在app组件和login组件的build.gradle里,添加对lib组件的依赖:
implementation project(':componentlib')
有了这三个组件,就可以开始进行组件化的举例开发了。
我们看到,上面两个组件的gradle文件里,各种版本号都是数字的形式存在的,比如这两处:
各个组件如果用的gradle版本,第三方库的版本等不一致的话,很容易出现问题,那怎么来统一管理这些版本号呢?
一个简单的解决方案就是,在配置文件里,用变量来定义这些版本号,然后进行引用。我们可以在项目的gradle.properties文件里添加定义:
compile_sdk_version = 26
support_version = 26.1.0
然后,就可以把各个组件gradle文件里的版本号做替代了,结果如下;
compileSdkVersion compile_sdk_version.toInteger() // 用toInteger()转为数字
api "com.android.support:appcompat-v7:$support_version" // 需要用到$符号表示引用,同时使用双引号
注意,这里lib组件依赖support库,换成了api关键字,而没有用implementation,因为api的意思是,如果login组件依赖了lib组件,那么也就依赖了support库。所以login组件里对support库的依赖就可以去掉了。
这样,就实现了各个版本号的统一管理。
要满足这个要求,我们可以定义一个开关来控制,开关仍然可以放到gradle.properties内容里。比如控制login组件是否能单独运行的开关:
loginRunAlone = false
如何使用这个开关呢?因为login组件如果要集成,那么1,它必须转换成library,2.它的applicationId也要去掉,3.manifest文件要换。
第1个和第2个,我们这么改写login组件的build.gradle文件:
if (loginRunAlone.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
if (loginRunAlone.toBoolean()) {
applicationId "example.hp.componentlogin"
}
这样就实现了application和library的转换。
第3点,我们可以把lib组件的AndroidManifest.xml文件复制到login组件里,新建一个manifest文件夹来存放,用于作为library集成。原先的用于作为application单独运行。这两个manifest文件如何指定呢?仍然是修改login组件的build.gradle文件,在“Android”那一段里加上这些:
sourceSets{
main {
if (loginRunAlone.toBoolean()) {
// 指定这个作为单独运行时的manifest
manifest.srcFile "src/main/AndroidManifest.xml"
} else {
// 指定这个作为集成时的manifest
manifest.srcFile "src/main/manifest/AndroidManifest.xml"
}
}
}
这样第二个问题也就解决了。通过修改loginRunAlone为true或false来转换login组件是否能单独运行。
1.设计组件化的一个初衷就是要避免组件间互相依赖。但是我们在写代码时,通过R.xxx.xxx之类很容易就会引用到其它组件的资源。比如login组件和app组件里都有布局文件叫activity_login.xml,那就很容易混淆。怎么解决呢?
一个方案,可以在组件的build.gradle里,加上resourcePrefix,如下是对login组件的修改:
这个意思是,所有的xml文件及xml里的资源命名,都要求带上"login_"前缀,否则就会出现警告:
其他组件也可以带上其他自定义的前缀。按这样命名,资源文件就不会重名了。
但是drawable文件不会有提示。所以图片的命名,就要靠我们自己制定和执行规范了。
2.另外,我们在app组件里,可能一不小心就通过R.layout.activity_login对login组件的布局文件进行了引用,就算能正常使用,也其实是违背了我们的设计初衷的。那么怎么避免呢?
为避免组件间失误的引用,可以把依赖关系改成runtimeOnly。
比如login组件如果集成的话,是要被app组件依赖的。app组件的build.gradle里原本这么写:
implementation project(':componentlogin')
如果要避免app组件对login组件的引用,可以在app组件的build.gradle里,改成这样:
runtimeOnly project(':componentlogin')
这样改后,在app组件里,就没法引用login组件的类和资源了。
这一节要讲的比较多,请看下一篇文章。
Android组件化开发的实现(二)Android组件之间页面如何跳转和传递数据
本文所对应的示例代码,已经上传到这里:
https://download.csdn.net/download/fenggering/10876548