Android组件化开发填坑记录
Android组件化开发通常也说模块化开发,使用Android Studio做开发时,通常组件化的实现就是在一个工程项目中同时包含若干个application模块和若干个library模块,还会引用一些第三方类库或者依赖。每一个application模块通常可以编译成一个应用,而library模块一般不用于生成可执行的应用,而是生成jar/aar类库,或者被同一项目下其他的library或application模块引用。
模块化的原因
模块化会增加编译负担,造成一些不必要的错误,所以,对于功能单一的应用,自然没有必要做模块化,所有代码写在一个app模块就行。但是对于复杂的业务场景,比如需要同时维护多个功能类似,但又有一些差别的应用,或者应用的功能模块需要经常进行定制化裁剪,或者多人协同开发功能模块,这些场景下使用组件化可以方便就行功能复用和裁剪,每个功能模块独立开发,互不干扰,提高开发效率。
组件化遇到的问题
一.第三方类库依赖问题
1.远程依赖问题
一般来说,远程依赖是最简单的,只需要配置路径即可,例如:
1 | compile 'com.android.support:design:25.3.1' |
如果在library模块中引用了一次,引用该library的其他模块无需声明,也可以使用这个远程依赖。若两者使用了不同版本的相同依赖,最好统一成同一个版本,以免二者发生冲突。
2.本地依赖问题
如果依赖本地的一个aar或者jar包,在library模块中引用一般没有问题,例如下面代码,我们把aar,以及native二进制的目录armeabi,armeabi-v7a等全放在library模块/libs目录下:
1 | apply plugin: 'com.android.library' |
这时,library模块可以正确编译,aar也生成出来了,但是引用它的app模块报错了:
1 | Error:Failed to resolve: :faceplatform-release: |
app模块编译也需要这个依赖库,但是找不到。这时,你可以把所有的aar复制一份到app模块里,这样编译没问题了,但是造成了很多重复文件,有个更好的办法可以不用拷贝文件,修改app模块的gradle即可:
1 | apply plugin: 'com.android.application' |
这样的意思是去library模块/libs目录下去找找依赖吧,这样就能找到需要的本地依赖库了。
二.Butter Knife在library模块使用的问题
Butter Knife是一个View注入框架,有了它,让我少写了很多findviewById(),在app模块中使用很简单:
1 | dependencies { |
代码中使用
1 | class ExampleActivity extends Activity { |
但如果要在library模块中使用,不但要修改gradle,java代码也要修改如下,这些github上都有说明:
https://github.com/JakeWharton/butterknife
- 需要使用gradle插件做一些预处理:
插件声明在library模块中使用插件:1
2
3
4
5
6
7
8buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1'
}
}1
2apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife' - 把R改成R2:
1
2
3
4
5class ExampleActivity extends Activity {
@BindView(R2.id.user) EditText username;
@BindView(R2.id.pass) EditText password;
...
}
三.资源ID常量的变化
在library模块中声明的资源ID,不能使用switch/case来判断,例如:
1 | public void onClick(View src){ |
上面的写法是错误的,android studio会提示错误,原因是资源ID在app模块中是声明为final的,也就是无法再改变的,而library模块中并没有声明final,只有在最终编译成apk的时候才会被统一进行确定,如果事先声明了final,多个模块编译到一个apk时难免互相有ID重复,或者与依赖库或app中的ID重复,这样就会造成错误。因此就不能采用switch/case来判断id了,而用if/else就没毛病了。
1 | public void onClick(View src){ |
上面butterknife的问题,也是由于这个原因,所以butterknife会用gradle插件重新生成一个资源文件R2,在R2中的ID都是用final来修饰的,所以就可以解决这个问题了。
四.接入GoogleService推送
使用google推送,或者是Firebase,就需要接入GoogleService,按照Firebase官网的指导,接入方法是先添加Gradle插件:
1 | buildscript { |
并且使用插件:
1 | apply plugin: 'com.android.application' |
这个插件必须只能使用在app模块,如果使用在library模块是要报错的。但是,如果我们的Firebase接收消息的业务逻辑写在了library模块,就需要在library模块中引用firebase的依赖库,而在app模块中apply plugin,google-services.json文件也必须放到app模块下,才能保证顺利编译。
1 | dependencies { |
五.资源覆盖问题
过去的单模块编写,同一资源目录下不允许出现两个同名的资源,但是在多模块的情况下,app模块的assets、res下的颜色,图片,字符串等都会覆盖library模块或者远程依赖带过来的同名资源,这样就可以方便的在app模块中修改library已定义好的主题,字符串等。