美文网首页程序员
站在巨人肩膀上,打造一个MVVM架构的“爱普”

站在巨人肩膀上,打造一个MVVM架构的“爱普”

作者: 肖某 | 来源:发表于2018-12-20 10:05 被阅读0次

用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的区别

image
image
 上面两张图描述的是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

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工作,所以时隔半年多才更新了这篇文章。这篇文章的诞生,我是要感谢公司的,公司给了一个全新的项目让我做,既然是全新的项目,那我也要用点我之前一直想尝试,但一直没机会用的技术来实现它!就这么简单!前期框架搭建起来的时候,遇到了有一些坑,都被我填平了,后期的坑,我没办法预测到,但我也做好了填平它的准备。坑越多,成长越多!骚年,加油吧!明天会更好!真好!(手动滑稽)

相关文章

  • 站在巨人肩膀上,打造一个MVVM架构的“爱普”

    用kotlin打造一个MVVM架构且模块化的“爱普”我的博客 一、什么是MVVM? 1、MVVM介绍  MVVM是...

  • 站在巨人肩膀上

    题目:站在巨人肩膀上 书目:《见识》 进度:全书 字数:757 001白天不懂夜的黑 因为成长环境不同,所处的阶段...

  • 站在巨人肩膀上

    一位50多岁的长者语重心长的提醒我:“你们现在的年轻人真是太幸福了,我们过去要想学点师傅的‘绝招’,起码要给师傅端...

  • 站在巨人的肩膀上

    回想起小时候的教室,黑色的,需要每周或每月刷墨汁的黑板,绿色的布满刻痕的双人书桌,和早已分辨不出颜色的长条...

  • 站在巨人的肩膀上

  • 站在巨人的肩膀上

    在古代,只智者——思想家——哲学家几乎是一个同义词。哲学,他本来的意义就在于通过事物的表象来把握事物的本质,通过规...

  • 站在巨人的肩膀上

    01坚信读书的力量 牛顿名言:如果我能比别人看得更远,是因为我站在巨人的肩膀上。 从书里能学到别人几十年的修行,可...

  • 站在巨人的肩膀上

    其实这个年代少有大师是有原因的,可能浮躁是一些人在成为大师路上越不过去的坎,对于我们也是一样,难的有潜心下来学习的...

  • 站在巨人的肩膀上

    文/雨竹 喝酒我喜欢朋友三两一起小酌,或多至6人,昨天与跑友喝酒聊天已至深夜,是共同的...

  • 站在巨人的肩膀上

    “我是站在巨人的肩膀上……” 听到这句话的第一反应是,有人又在装…… 这是现有教育对我们的洗脑,而使我们忽视了其巨...

网友评论

    本文标题:站在巨人肩膀上,打造一个MVVM架构的“爱普”

    本文链接:https://www.haomeiwen.com/subject/piiekqtx.html