用 kotlin 打造一个 MVVM 架构且模块化的 "爱普" 我的博客 https://www.xiaoqqq.com/
一, 什么是 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 的区别
image
image
上面两张图描述的是 MVVM,MVP,MVC.MVC 就不讲了, 反正我现在搭建项目的话, MVC 是完全不会考虑了! 讲讲 MVP, 用过 MVP 的都知道, MVP 的思想其实是面向接口编程, View 和 Model 层都是 interface 来的, 也就是说, 我们在 IView 或者 IModel 里面, 添加一个 xxx()方法, 我必须在对应的实现类里分别去实现这个 xxx()方法. 不知道你们感觉如何, 反正我感觉用着比较难受! 那么 MVVM 不用这样嘛? 答案很明确 -- 不会!!!
Android 里面实现 MVVM 需要用到一个东西, 那就是 databinding.databinding 能把数据和试图进行绑定, 只要获取到数据, 试图就会跟着变化, 这样节省了很多代码, 用起来非常方便. 用过前端框架 (vue 等) 的人应该很清楚, 在我们 Android 里面是这样写:@{ http://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
image
image
- 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 工作, 所以时隔半年多才更新了这篇文章. 这篇文章的诞生, 我是要感谢公司的, 公司给了一个全新的项目让我做, 既然是全新的项目, 那我也要用点我之前一直想尝试, 但一直没机会用的技术来实现它! 就这么简单! 前期框架搭建起来的时候, 遇到了有一些坑, 都被我填平了, 后期的坑, 我没办法预测到, 但我也做好了填平它的准备. 坑越多, 成长越多! 骚年, 加油吧! 明天会更好! 真好!(手动滑稽)
来源: http://www.jianshu.com/p/aada4211eb5d