用kotlin打造一个MVVM架构且模块化的“爱普”我的博客
一、什么是MVVM?
1、MVVM介绍
MVVM是Model-View-ViewModel的简写,Model是数据层,View是视图层,ViewModel是Model和View之间的桥梁。当然,你也可以理解成Model就是数据(这个数据可以是本地数据,也可以是网络数据),View就是activity或者fragment,ViewModel里面都是java/kotlin代码,处理一些业务逻辑。<label style="color: red">注意,ViewModel不能持有任何View的上下文,或者控件对象。否则,就违背了其设计初衷,Model和View完全解耦</label>。
2、MVVM、MVP、MVC的区别
imageimage
上面两张图描述的是MVVM、MVP、MVC。MVC就不讲了,反正我现在搭建项目的话,MVC是完全不会考虑了!讲讲MVP,用过MVP的都知道,MVP的思想其实是面向接口编程,View和Model层都是interface来的,也就是说,我们在IView或者IModel里面,添加一个xxx()方法,我必须在对应的实现类里分别去实现这个xxx()方法。不知道你们感觉如何,反正我感觉用着比较难受!那么MVVM不用这样嘛?答案很明确--不会!!!
Android里面实现MVVM需要用到一个东西,那就是databinding。databinding能把数据和试图进行绑定,只要获取到数据,试图就会跟着变化,这样节省了很多代码,用起来非常方便。用过前端框架(Vue等)的人应该很清楚,在我们Android里面是这样写:@{xxx.name}。but,我在这个项目中,我保留了这种写法,为什么呢?因为我对MVVM理解还不是很深,我需要有个过渡阶段,如果从一开始,就用这种写法,后期遇到bug,我可能没办法很快的去修复它。所以,我用到了kotlin,kotlin写法简洁和无需findViewById,我感觉能让我过度一下。(这也是我为什么这个项目会用kotlin来写的重要原因)
二、为什么用kotlin?
1、kotlin--自我介绍
kotlin是谷歌亲儿子,已经正式成为Android官方的开发语言,由 JetBrains 开发,是一个用于现代多平台应用的静态编程语言。Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。
2、kotlin相比Java的优势
- 谷歌亲儿子,不久的将来,肯定上位;
- 写法简洁:大大减少样板代码的数量;
- 安全: 避免空指针异常等整个类的错误;
- 互操作性: 充分利用 JVM、Android 和浏览器的现有库;
3、如何从java过渡到kotlin?
在Android studio里面,可以直接将java代码转换成kotlin,目前,我是用一个非常笨的方法:写一个test java类,把我想实现的代码写出来,然后利用Android studio转换成kotlin,然后再到kotlin类里依葫芦画瓢抄一遍。抄多了,自然而然就会写了。
三、模块化怎么实现的?
1、模块化到底是什么意思?
将整个项目分成若干个子模块,每个模块干自己的事情,最后将所有子模块组合成一个APP。这就是我对模块化的理解!当然,里面会有一些细节的东西,和一些面向对象的思想,在后面我会介绍的!<label style="color: red">小插曲:我之前写的关于组件化的文章,其实应该是模块化,现在我对组件化的理解是:比模块化更小,更具体的东西,一个自定义控件,或者一个公共的控件,才是组件化(不难排出以后我的想法还会变哦!哈哈……)</label>
2、模块化的优势所在
- 可复用:写好的公共模块,可复用,减少重复代码;
- 可组装:在宿主APK里,可灵活的组装各个子模块,组装成一个APK;
- 可独立:可以成为一个APK单独运行,单独测试,不受宿主APK的影响,这样的好处是多人开发一个项目时,各自负责的模块之间影响能降到最低,提高开发效率;
3、ARouter在模块化中的重要指数
ARouter在项目模块化中起到了非常关键的作用,起作用可以理解为,如果没有阿里巴巴的ARouter,你项目的模块化可能实现起来就非常难了(当然不排除会有其他优秀的中间件出现咯)。ARouter github:https://github.com/alibaba/ARouter ARouter简单的说,将activity、fragment之间的跳转由传统的intent变成了url,非常之方便。我们也知道传统的intent显示跳转,需要activity的上下文,这样的话,在不同模块之间会持有activity上下文的引用,这样module和module之间就耦合了,这不是我们想看到的。ARouter,刚好解决了这个痛点,并且跳转很方便,代码很简洁,易于维护和扩展。
这里关于ARouter的用法,不多说了,网上很多相关的文章可以查阅。总之,你要想项目模块化,ARouter不可少!
四、项目整体架构
<label style="color: red">该文章的核心来了!我这个项目的整体架构是什么样的?实现思路是怎样的?用到了哪些技术?我都会在这里尽可能的说清楚。</label>
<label style="color: red">先不说话,请看👇</label>
4.1 APP架构
image<label style="color: red">lib_common</label>(上图的Commono):公共的lib,里面主要存放一些Base类、工具类以及一些公共组件等。
<label style="color: red">lib_coremodel</label>(上图的coreViewModel):核心的ViewModel库,里面只放对应的ViewModel类。
<label style="color: red">lib_opensource</label>(上图的openSource):底层资源库,里面只做一件事,依赖一些需要的lib,如:网络请求库、图片加载等
<label style="color: red">宿主APP</label>:每个module(上图中的home、regist、chat和……)相对独立,各自完成自己的功能,可以library和application随意切换,也就是说独立时,他是一个APP,整合时,他是一个module
<label style="color: red">一层层依赖,最上层module只依赖lib_common!</label>lib_common依赖lib_coremodel,lib_coremodel依赖lib_opensource。宿主APP依赖各个module
4.2 APP项目结构
image从项目结构中可以看到,module_home和module_login是我现在项目中的两个子模块,module_home模块实现首页相关的业务逻辑,module_login模块实现登陆注册相关的业务逻辑,并且这两个模块是application类型(当然我们是可以通过参数来控制并实现其application和library的灵活切换),当它们是application的时候,我们可以把它当成是一个可单独运行的APK,既然可以单独运行,那肯定也是可以单独测试的。后期,我会把项目按模块划分,届时就会有很多module_xxx,待所有module都开发完成,最后组装一下,就可以得到我们想要的APK。
4.3 gradle.properties设置
// 整合模块时为true,单独APP运行时为false
isLoginModule=false
isHomeModule=false
4.4 module_login
imageimage
if (!isLoginModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 26
defaultConfig {
if (!isLoginModule.toBoolean()) {
applicationId "应用程序包名"
}
minSdkVersion 22
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
android {
sourceSets {
main {
if (!isLoginModule.toBoolean()) {
manifest.srcFile 'src/main/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
}
}
}
}
4.5 宿主APK build.gradle
// 登陆模块
if (isLoginModule.toBoolean()) {
implementation project(':module_login')
}
// 首页模块
if (isHomeModule.toBoolean()) {
implementation project(':module_home')
}
通过上面的图文介绍,关于项目架构的整体思路和过程应该是比较清晰了吧。原来这样做下来,就能构建出一个模块化的MVVM项目。当然,在构建过程中,还应用了一些其他的技术,让项目看上去更加perfect。下面就来简单的说说,用到了哪些技术。具体的研究,以后再写吧!
4.6 项目中用到的技术
ARouter在上面说过了,这里就跳过。下面介绍一下LiveData和ViewModel
4.6.1 LiveData
LiveData 是一个可以感知 Activity 、Fragment生命周期的数据容器。当 LiveData 所持有的数据改变时,它会通知相应的界面代码进行更新。同时,LiveData 持有界面代码 Lifecycle的引用,这意味着它会在界面代码(LifecycleOwner)的生命周期处于 started 或 resumed 时作出相应更新,而在 LifecycleOwner 被销毁时停止更新。
4.6.2 ViewModel
ViewModel 将视图的数据和逻辑从具有生命周期特性的实体(如 Activity 和 Fragment)中剥离开来。直到关联的 Activity 或 Fragment 完全销毁时,ViewModel才会随之消失,也就是说,即使在旋转屏幕导致Fragment 被重新创建等事件中,视图数据依旧会被保留。ViewModels 不仅消除了常见的生命周期问题,而且可以帮助构建更为模块化、更方便测试的用户界面。
class HomeViewModel : BaseViewModel() {
private var mLiveData: MutableLiveData<HomeBean>? = null
/**
* 这个getLiveData()方法可以自定义为其他,应用场景:一个月面可能同时有多个请求,这些多个的请求,可以分开定义liveData
*
* @return liveData
*/
val liveData: LiveData<HomeBean>
get() {
if (mLiveData == null) {
mLiveData = MutableLiveData()
initData()
}
return mLiveData!!
}
/**
* 默认的数据,不需要请求数据的页面,这个方法块可以为空
*/
private fun initData() {
}
/**
* 加载数据,该方法可以自己衍生为网络请求回来的数据
*/
fun loadData() {
val homeBean = HomeBean()
homeBean.name = "loadUserName"
homeBean.address = "male"
mLiveData!!.value = homeBean
}
}
class MainActivity : BaseActivity<HomeViewModel>() {
override fun createViewModel(): HomeViewModel {
return HomeViewModel()
}
override val layoutId: Int
get() = R.layout.activity_main
private lateinit var viewModel: HomeViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 看这里,看这里,看这里
viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
viewModel.liveData.observe(this, Observer { home ->
// 得到通知时,修改home_tv_display这个TextView的text(这里体现的是kotlin的美!)
home_tv_display.text = home!!.name
})
// 这上面就是LiveData的使用,下面代码是viewModel去做具体的业务逻辑示例
home_btn_load.setOnClickListener({
Toasts.showMiddle(this@MainActivity, "loadData")
viewModel.loadData()
})
}
}
五、总结
由于近期工作太忙,再加之工作上很多东西都是cv工作,所以时隔半年多才更新了这篇文章。这篇文章的诞生,我是要感谢公司的,公司给了一个全新的项目让我做,既然是全新的项目,那我也要用点我之前一直想尝试,但一直没机会用的技术来实现它!就这么简单!前期框架搭建起来的时候,遇到了有一些坑,都被我填平了,后期的坑,我没办法预测到,但我也做好了填平它的准备。坑越多,成长越多!骚年,加油吧!明天会更好!真好!(手动滑稽)
网友评论