Android组件化框架搭建
前⾔
组件化是什么,是把⼀个功能完整的 App 或模块拆分成多个⼦模块, 表现在androidStudio项⽬⼯程⾥就是分多个module。每个⼦模块可以独⽴编译和运⾏, 模块之间可以任意组合成另⼀个新的 App 或模块, 每个模块不必须相互依赖但可以相互调起和通信。
组件化的意义,对于⼀个⼩型项⽬来说可能觉得多此⼀举,但是对于⼀个中型以上的项⽬,组件化还是⾮常有意义的。APP版本不断的迭代,新功能的不断增加,业务也会变的越来越复杂,的代码也变的越来越多,代码耦合严重,影响开发效率,增加项⽬的维护成本,编译代码的时间长,每修改⼀处代码后都要重新编译整个项⽬。组件化的出现就是解决以上问题,并且还具备⾃由组装成新的模块的优点。
我研究组件化主要还是为了技术储备和⽀持新业务的开发,对于主项⽬要使⽤的话,就相当于项⽬重构,⽐较费时费⼒,在各组都有KPI的情况下是不会给我们时间重构的,闲话不多说,下⾯介绍组件化框架和搭建过程中注意的问题。
组件化架构
直接上图:
21.png
架构介绍:
如图⼀共分了4层:应⽤层、业务组件、功能组件、基础组件。
应⽤层:我们的app壳⼯程,负责管理各个业务组件,和打包apk,没有具体的业务功能。
业务组件:具体业务⽽独⽴形成⼀个的⼯程,可以单独运⾏提供功能。
功能组件:APP的某些基础功能,可单独编译,但不会单独发布提供功能apk。
基础组件:开源的第三⽅的库。
Eventbus 是⽤来各层之间的通信,组件路由是⽤来实现组件之间的通信和调起。
⽹上有的⽂章有不同的分层发,有⼈分三层把业务组件和功能组件统称组件层。有⼈对业务组件进⾏细分,如Main组件。整体是⼀样的,关键⼤家能理解整个架构。
组件化项⽬结构介绍
直接上图:
223.png
⼤家看到有多个module,以app_开头的是可以单独打包发布的module,也就是app壳module,和业务组件module,module开头的是不单独打包发布的组件,就是功能组件。我们⼀⼀说明:
app:app壳,就是我们主项⽬。android retrofit
app_radio:电台业务模块,可以单独作为电台app发布,也是app主项⽬的⼀个功能模块。
module_core:项⽬的基础核⼼组件,说是基础,他是对基础组件的封装,包括架构图中的Glide、Retrofit、Rxjava等的封装,提供基础功能。说是核⼼,他是对项⽬基础架构的封装,这⾥使⽤的是MVP架构。
module_commons:封装组件共⽤的类和资源,各组件模块解耦之后,避免不了⼀些共⽤的类和资源,⽐如实体类、错误页、dialog 等。
module_router:封装组件路由,实现组件调起和通信。
module_share / module_playserservice:我们项⽬⽤到的功能组件,不是必要的。
⽹上有同学有不同的module分发,⽐如module_commons进⼀步细分公共的类module和资源module。⼤家可以根据实际情况细分。到这⾥⼤家会发现组件化的⼀个弊端,就是项⽬会有很多的module。
组件化搭建注意问题
组建如何单独编译
思路就是在adle的区分是apply plugin: 'com.android.application'还是apply plugin: 'com.android.library'。具体实现:
在gradle.properties⽂件添加⼀个判断属性
isBuildAsModule=false
在adle⾥根据属性加载不同插件:
Boolean()){
apply plugin: 'com.android.application'
}else{
apply plugin: 'com.android.library'
}
在两种情况下l⽂件是有差别的。作为独⽴运⾏的app,有⾃⼰的Application,要加Launcher的⼊⼝intent,⽽作为library不需要。所以需要写两个不同的l即可,通过isBuildAsModule加以区分。
if (Boolean()) {
manifest.srcFile 'src/main/l'
} else {
manifest.srcFile 'src/main/l'
}
这种⽅式同时避免了l清单⽂件冲突问题。
其他需要区分两种编译模式,原理相同。
基础库和SDK版本号统⼀
这是多module开发需要解决的问题,不同module的依赖sdk版本不⼀致或者引⼊的库版本不⼀致会导
致编译问题和兼容性问题。解决办法是在主项⽬最外层⽤⼀个l⽂件来统⼀管理基础库和sdk的版本。l部分代码如下:
def retrofit_version = '2.3.0'
def okhttp_version = '3.9.0'
def dagger_version = '2.11'
def autodispose_version = "0.5.1"
def support_version = '27.1.0'
def espresso_version = '2.2.2'
def glide_version = '4.6.1'
def butterknife_version = '8.8.1'
def routerVersion = "1.2.4"
def routerCompilerVersion = "1.1.4"
< {
android = [
compileSdkVersion: 27,
buildToolsVersion: "27.0.3",
applicationId : "com.taihe.music.mvpsample",
minSdkVersion : 16,
targetSdkVersion : 27,
versionCode : 1,
versionName : "1.0"
]
dependencies = [
/
/android-support
"support-v4" : "com.android.support:support-v4:${support_version}",
"appcompat-v7" : "com.android.support:appcompat-v7:${support_version}",
..........
使⽤l,时在最外层adle配置config⽂件:
apply from: "adle"
具体引⼊基础库的地⽅在各module的adle⽂件⾥:
android["minSdkVersion"]
android["targetSdkVersion"]
android["versionCode"]
android["versionName"]
.
....
//glide
dependencies["glide"]
dependencies["glide-compiler"]
组件之间资源名冲突
组件之间如果资源命名相同,就会产⽣冲突,解决这个问题最简单的办法就是在项⽬中制定资源⽂件命名规范,⽐如app_radio组件所有资源以radio_开头,所有开发⼈员必须遵守规范。
当然,gladle也给我提供了解决⽅案,就是在adle中添加如下的代码:
resourcePrefix "radio_"
设置了这个属性后,所有的资源名必须以指定的字符串做前缀,否则会报错。⽽且这种形式只能限定xml⾥⾯的资源,并不能限定图⽚资源,我们仍然需要⼿动去修改资源名;所以不推荐使⽤这种⽅法来解决资源名冲突。
基础组件依赖问题
所有的基础组件我们封装在module_core中,这样就存在⼀个问题,我们项⽬依赖了module_core就默认依赖了所有的基础组件,但实际情况中我们是不需要依赖所有的,或者module_core中的基础组件和其他模块有冲突的情况。所以我们需要排除掉⽤不到和重复的
库,Gradle⽀持两种排除⽅式,根据组件名排除或者根据包名排除,代码如下:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion") {
exclude module: 'support-v4'//根据组件名排除
exclude group: 'android.support.v4'//根据包名排除
}
}
组件之间跳转和通信
@Route(path = RouterConstants.RADIO_HOME_ACTIVITY)
public class HomeActivity extends BaseActivity<HomePresenter> implements HomeContract.View {
@BindView(R2.id.btnUserInfo)
Button button;
@BindView(R2.id.ivTest)
ImageView imageView;
........
调起HomeActivity的代码:
.build(RouterConstants.RADIO_HOME_ACTIVITY)
.
withObject("user", userModel)
.navigation();
可以看出组件路由需要定义常量RouterConstants.RADIO_HOME_ACTIVITY来关联调起页⾯,withObject("user", userModel)是页⾯间传递的数据。
⼿动单独编译
组件化的⼀个优点就是可以单独编译,但是在开发过程发现有的时候修改了模块module⾥⼀些配置⽂体之后,编译主app,模块module不会重新编译,导致修改⽆效。所以在修改了配置⽂件之后最好⼿动编译module确保修改有效。单独编译的⼊⼝在下图:
1538204810(1).png
动态变化⽹络baseUrl
以app_radio配置baseUrl为例:
RADIO_BASE_API为具体的baseUrl,RADIO_DOMAIN_NAME为对应的key。
具体使⽤Retrofit来定义请求接⼝时:
@Headers({DOMAIN_NAME_HEADER + RADIO_DOMAIN_NAME})
@GET("users/{user}")
Maybe<UserInfo> getUserInfo(@Path("user") String user);
这样就可以实现baseUrl的动态变化。同理如果其他的⽹络配置不同也是使⽤是形式来修改,⼤家可以参考RetrofitUrlManager的实现。
本⽂从整体上介绍了组件化的搭建问题,没有具体到基础库和框架的封装,也就是module_core的搭建和使⽤。想要熟悉整体框架或者使⽤,⾸先要熟悉第三⽅库的使⽤和原理,封装的过程就是改造的过程所以要了解基本原理;其次再熟悉module_core的封装代和使⽤,最后才是本⽂提到的组件化过程中的问题解决。
跟本⽂⼀样,⼤部分开源组件化项⽬,基础库⽤到了Dagger、Rxjava。这两个开源项⽬需要⼀定的学习成本,建议⼤家择情⽽定是否使⽤。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论