Android端MVVM从入门到实战(第一篇) - MVVM和四大官方组件

这篇具有很好参考价值的文章主要介绍了Android端MVVM从入门到实战(第一篇) - MVVM和四大官方组件。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、MVVM简介

MVVM是相对于MVC和MVP的一个概念,是一种架构模式。

1.1 MVC

传统的MVC中,View改变通知Controller进行处理,Controller处理结束后通知Model层更新,Model层更新以后通知View层渲染,指令单项流动,角色分工明确。但是MVC有三个缺点,1、三个角色互相持有对方依赖,因此很难复用其中任意一方;2、开发时必须三个模块同步开发,否则很难相互匹配;3、由于每一个角色的改变都会直接或间接的影响另外两个角色,所以任何改动都必须考虑全盘影响。

android mvvm实战,android,java,ui,Powered by 金山文档

1.2 MVP

MVP解决了以上三个问题,MVP中的Presenter层相当于MVC中的Controller层,但有一个变动:Presenter分别和Model层以及View层双向交互,而Model层与View层之间不再直接交互,并且Presenter来定义Model层和View层各自要实现的功能。这个变动解决了之前所提的三个问题,首先Model层和View层可以任意替换,只要代替者能实现Presenter层定义的接口即可,这样保证了Model层和View层的可复用性;其次在Presenter层定义了Model层和View层需要实现的功能后,Model层和View层可以分别开发,有Presenter层去处理两者适配的问题;最后Model层和View层脱离以后任意一方的改动只要Presenter层进行适配即可,不会再影响到对方,降低了修改代码时的影响范围。

android mvvm实战,android,java,ui,Powered by 金山文档

1.3 MVVM

MVVM是MVP模式进一步发展的产物,通过语言或框架的支持,开发人员不需要再手动处理Model改变以后View的更新,而是通过订阅-观察的模式让View在Model改变时自动更新。这个模式虽然有一定的学习成本,但优点(尤其是在Android端)也清晰可见:1、继承了MVP的所有优点,拥有较好的可复用性和可维护性,并且view层和model层可以分别开发;2、方便测试,由于Model和View在框架层面上进行绑定,理论上只要数据正常且绑定的方式合理,View显示就不会有问题,这样可以针对Model层和ViewModel层进行单元测试,而不用再考虑View层;3、Android中的生命周期、activity重建等问题不需要开发人员考虑,由jetpack提供的组件进行处理。

android mvvm实战,android,java,ui,Powered by 金山文档

Android端的MVVM架构的实现基于jetpack组件包中的四个组件:Databinding、LiveData、ViewModel、Lifecycle,接下来我们在一个简单的案例中分别了解一下这四个组件。

  1. 分别讲解

参考代码地址:https://github.com/guoergongzi/GMVVMDemo/tree/main

2.1 LifeCycle

在Android代码解耦的过程中,处理生命周期是一个很核心的问题,一个普通组件必须要依赖系统组件(如Activity、Fragment)的调用才能知道自己方法运行的时机,LifeCycle这个组件正是为了解决这个问题而生。

LifeCycle提供了两个接口:LifecycleOwner和LifecycleObserver;只要让实现了LifecycleObserver接口的普通组件去订阅实现了LifecycleOwner的系统组件,就可以让该组件可以感知到系统组件的生命周期,并在对应的时机去处理自身的逻辑。

下图中可以看到,我们现在版本源码中的Activity已经实现了LifecycleOwner,因此我们只要编写LifecycleObserver的实现并让它订阅Activity即可。同理Fragment也实现了这个接口,这里只以Activity做一个演示。

android mvvm实战,android,java,ui,Powered by 金山文档

我们编写如下LifecycleObserver实现类:

public class TestClass implements DefaultLifecycleObserver {

    @Override
    public void onResume(@NonNull LifecycleOwner owner) {
        DefaultLifecycleObserver.super.onResume(owner);
        Toast.makeText((Context) owner, "测试内容", Toast.LENGTH_SHORT).show();
    }
}

在activity的onCreate()方法中用以下代码订阅:

getLifecycle().addObserver(new TestClass());

这样当Activity显示时我们的测试Toast就会显示出来,相当于直接写在Activity的onResume()回调中。

这里只演示了一个简单的Activity中使用Lifecycle的方式,之后的文章中我会详细的分享关于Lifecycle的更多知识,这里我们先接着了解我们的下一个组件。

参考代码Module:glifecycledemo

2.2 ViewModel

ViewModel是Google提供的Android端实现MVVM中的VM层的标准方式,它在创建时需要一个LifecycleOwner作为参数,在这个LifecycleOwner销毁时它自己也会销毁(Activity旋转造成的Activity销毁和重建并不会触发)。

把逻辑放在ViewModel中既可以给Activity(或Fragment等其它LifecycleOwner)瘦身,又能避免Activity旋转造成数据丢失。

下面我们用一个小案例来了解一下ViewModel的基本使用方式,首先先创建一个ViewModel子类,写一些逻辑在里面。

public class TestViewModel extends ViewModel {

    private Timer timer;
    private int timeCount = 0;

    public void startTimer() {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                timeCount++;
                Log.v("G", "timeCount = " + timeCount);
            }
        };
        timer = new Timer();
        timer.schedule(timerTask, 1000, 1000);
    }

    public int getTimeCount() {
        return timeCount;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        timer.cancel();
    }
}

然后在Activity的onCreate中写下以下代码来绑定Activity和ViewModel。

TestViewModel viewModel = new ViewModelProvider(NewActivity.this).get(TestViewModel.class);
viewModel.startTimer();

我们可以再写一个按钮来测试getTimeCount()方法。

Button timeButton = findViewById(R.id.btn_view_model_demo);

timeButton.setOnClickListener(view -> {
    Toast.makeText(NewActivity.this, "计时到第" + viewModel.getTimeCount() + "秒", Toast.LENGTH_SHORT).show();
});

这样我们点击按钮时就可以看到Activity正确的获取到ViewModel中定义的值了。

在项目运行过程中,如果我们旋转一下手机画面,我们会发现timeCount依然在之前的数值上累积,而没有因为旋转归零,这点验证了ViewModel可以避免Activity旋转造成的数据丢失。我们退出Activity,会发现我们写在计时器里面的日志停下来了,说明Activity的销毁可以销毁ViewModel并触发它的onCleared()方法。

这里用一个简单的例子介绍了ViewModel,和Lifecycle一样,ViewModel还有很多用法和知识需要我们了解,但是我们把这部分也放在之后的文章中。另外我们还没有了解LiveData和DataBinding,接下来我们了解了这两个框架以后,我们会发现这个Demo里的ViewModel有更好的写法。

参考代码Module:gviewmodeldemo

2.3 LiveData

LiveData是一种可观察的数据存储器类,相比于其它可观察类,它拥有感知生命周期的作用,确保它仅在应用组件处在活跃中时才会被触发。

我们把ViewModel部分的案例稍加改造,用MutableLiveData来包装timeCount参数,通过setValue()或postValue()方法来更新数值。

public class TestViewModel extends ViewModel {

    private Timer timer;
    private MutableLiveData<Integer> timeCount = new MutableLiveData<>();

    {
        timeCount.setValue(0);
    }

    public void startTimer() {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                timeCount.postValue(timeCount.getValue() + 1);
            }
        };
        timer = new Timer();
        timer.schedule(timerTask, 1000, 1000);
    }

    public Z<Integer> getTimeCount() {
        return timeCount;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        timer.cancel();
    }
}

然后在Activity的onCreate()中添加ViewModel并找一个TextView来显示timeCount的值。

TestViewModel viewModel = new ViewModelProvider(MainActivity.this).get(TestViewModel.class);
viewModel.startTimer();

TextView textView = findViewById(R.id.tv_live_data_demo);
LifecycleOwner owner = MainActivity.this;
viewModel.getTimeCount().observe(owner, integer -> {
    textView.setText("计时到第" + integer + "秒");
    Log.v("G", "timeCount = " + integer);
});

运行起来后我们会发现,TextView的内容会不停的刷新,这就是LiveData的主要作用——在数值更新时通知注册的观察者。

到这里为止,LiveData看起来和EventBus之类的事件通知框架区别不大,那么为什么我们要在MVVM中使用这个组件呢?核心原因有几点:

1、LiveData在Activity等LifecycleOwner不活跃时不会发布通知,上图的案例中我们把日志打印的代码从计时器中移到了观察者回调中,我们把Activity退到后台时会发现日志不再打印了,这可以避免我们界面不可见时依然处理事件造成手机运行资源的浪费。

2、LiveData和Activity生命周期同步,不易发生内存泄漏,也不用再界面销毁时处理它。

3、我们的代码中声明timeCount对象时用了MutableLiveData类,提供get方法时却返回了LiveData类,MutableLiveData类是LiveData类的子类,它们的区别是MutableLiveData可以调用postValue()或setValue()方法更新内容,LiveData类却不行。从架构的角度看,这种方式保证了数据只会在VM层更新,不会被View层更新——因为View层通过get方法得到的是无法更新的LiveData类。

4、LiveData和我们之后要介绍的DataBinding可以实现配合。

可以看到,我们订阅LiveData时需要提供一个LifecycleOwner对象,就是这个参数让LiveData有了感知Activity生命周期的能力。我们在进行MVVM开发时有时候并不会直接用到LifeCycle,但它是支持Android端MVVM实现的重要组件,这也是这篇文章要介绍它的原因。

参考代码Module:glivedatademo

2.4 DataBinding

到目前为止,MVVM的核心特征——Model改变时数据自动变化我们仍然没有看到。当我们想要在textView中显示内容时,还是要在观察者模式里手动的调用setText,没错,这就是DataBinding这个组件为我们解决的问题。

首先我们要在module目录下的build.gradle文件中添加以下代码,允许项目使用Databinding

android {
		。。。
    // 允许项目使用databinding
    dataBinding {
        enabled = true
    }
}

然后我们更改一下上一个Demo中的TestViewModel,把返回的LiveData类型改成拼接好的字符串timeCountString:

public class TestViewModel extends ViewModel {

    private Timer timer;
    private final MutableLiveData<String> timeCountString = new MutableLiveData<>();
    private int timeCount = 0;

    {
        timeCountString.setValue("计时到第" + timeCount + "秒");
    }

    public void startTimer() {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                timeCount += 1;
                timeCountString.postValue("计时到第" + timeCount + "秒");
            }
        };
        timer = new Timer();
        timer.schedule(timerTask, 1000, 1000);
    }

    public LiveData<String> getTimeCount() {
        return timeCountString;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        timer.cancel();
    }
}

接下来我们要把界面的布局文件特殊处理一下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="<http://schemas.android.com/apk/res/android>"
    xmlns:app="<http://schemas.android.com/apk/res-auto>"
    xmlns:tools="<http://schemas.android.com/tools>">

    <data>

        <variable
            name="vm"
            type="com.gegz.gdatabindingdemo.TestViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@{vm.timeCount}"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

可以看到,我们的布局文件和不使用DataBinding时的样子有很大不同:

1、我们用layout包裹了原本最外层的布局,并且在原本最外层布局的同级添加了一个data标签,在其中用添加了一个name为vm、type为TestViewModel的variable标签。

2、我们没有给TextView设置Id,取而代之的是我们直接给它设置了text属性,并且在属性里使用了@{vm.timeCount}的写法。

最后我们在MainActivity的onCreate中加载ViewModel和DataBinding:

ActivityMainBinding mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
mBinding.setLifecycleOwner(this);
TestViewModel viewModel = new ViewModelProvider(this).get(TestViewModel.class);
viewModel.startTimer();
mBinding.setVariable(BR.vm, viewModel);

这里有几个需要注意的地方:

1、我们调用了DataBindingUtil的setContentView就不用再调用Activity的setContentView了,如果我们点进去看看源码,就会发现DataBindingUtil的setContentView调用了Activity的这个方法。

2、我们这里用到了一个ActivityMainBinding,但我们并没有声明它,不用担心,这个类是系统自动生成的。

3、我们给ActivityMainBinding的对象设置了viewModel作为数据源,这个地方也可以用普通的实体类,但使用ViewModel可以更好的解耦逻辑层的代码,并且能灵活的处理一些逻辑。

参考代码Module:gdatabindingdemo

3、总结

到最后一个Demo为止,我们已经把上面提到过的四个组件——LifeCycle、ViewModel、LiveData和DataBinding都用上了,并且了解了这四个组件各自扮演的角色。

结合我们文章开头对MVVM架构的介绍来分析我们这个Demo,会发现Demo中的Activity只负责将DataBinding、ViewModel和自己绑定起来,TestViewModel类中包含了我们这个界面的逻辑——计时并更新数据,而xml文件则通过框架的支持完成了界面的显示和更新,View层和ViewModel层之间的分工体现的十分明确。有一个遗憾是我们还没有一个独立的Model层,毕竟我们这个Demo的功能还太过简单,给它添加一个单独的Model层难免有过度设计之嫌,我们会在这个系列之后的文章中演示Model层的写法。

在这篇文章里,我们已经对android端的MVVM有一个基本的理解,并且实现了一个简单的小界面,但是很明显,这种程度的理解还远远不够我们去完成实际项目,我们还需要学很多知识,比如怎么加载网络图片,比如怎么编写fragment和recyclerView,比如我们之前提到过的Model层怎么设计,这些我们都会在之后的文章中介绍,希望大家多多点赞收藏,期待在下一篇文章中与大家讨论更详细的Android端MVVM知识。

注:出于篇幅考虑,本文中很多代码片段不完整,大家可以从Github上下载Demo学习。

参考文档:

知乎:MVC、MVP、MVVM -- 可多C

CSDN:三种架构模式——MVC、MVP、MVVM -- 非早起选手

CSDN:Android LifeCycle详解 -- 优雅的心情

CSDN:Android ViewModel详解 -- 赵彦军

CSDN:Android LiveData 详解及使用 -- 大肠包小肠|

CSDN:Android DataBinding的基本使用 -- 尹中文

CSDN:Android DataBinding 从入门到进阶,看这一篇就够 -- 程序员一东文章来源地址https://www.toymoban.com/news/detail-759279.html

到了这里,关于Android端MVVM从入门到实战(第一篇) - MVVM和四大官方组件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [Android 四大组件] --- Activity

    ​​Activity​​是一个Android的应用组件,它提供屏幕进行交互。每个Activity都会获得一个用于绘制其用户界面的窗口,窗口可以充满哦屏幕也可以小于屏幕并浮动在其他窗口之上。 一个应用通常是由多个彼此松散联系的Activity组成,一般会指定应用中的某个Activity为主活动,也

    2024年02月10日
    浏览(43)
  • [Android 四大组件] --- BroadcastReceiver

    BroadcastReceiver(广播接收器)即广播,是一个全局的监听器。 Android 广播分为两个角色:广播发送者、广播接受者。 广播按照类型分为两种,一种是全局广播,另一种是本地广播 全局广播:就是发出的广播被其他任意应用程序接收,或者可以接收来自其他任意应用程序的广播

    2024年02月10日
    浏览(47)
  • Android 四大组件之广播

    在Android应用开发中,广播组件是一项关键技术,被广泛用于各种任务和场景。无论是发送系统级广播,还是在应用内部实现组件间的通信,了解和掌握广播组件的工作原理和使用方法对于每个Android开发者来说都是至关重要的。本文旨在帮助你深入了解Android广播组件,并给出

    2024年02月06日
    浏览(46)
  • Android四大组件之服务

    为什么要使用服务呢? 从上面的文字说,我们知道这个服务是用于执行长期后台运行的操作。有些时候,我们没有界面,但是程序仍然需要工作。比如说,我们播放音乐,在后台播放音乐。比如说,我们下载任务,在后台下载文件。这些都是没有界面 的后台运行程序,这些

    2024年02月14日
    浏览(78)
  • Android 四大组件启动

    service: startService启动过程分析 - Gityuan博客 | 袁辉辉的技术博客 在整个startService过程,从进程角度看服务启动过程 Process A进程: 是指调用startService命令所在的进程,也就是启动服务的发起端进程,比如点击桌面App图标,此处Process A便是Launcher所在进程。 system_server进程: 系统

    2024年04月11日
    浏览(38)
  • Android中级——四大组件工作过程

    startActivity()最终都会调用到startActivityForResult() 上面调用Instrumentation的execStartActivity() 上面获取ActivityTaskManagerService,是IPC过程,调用其startActivity()启动,然后调到startActivityAsUser() 上面通过ActivityStartController获取ActivityStarter,调用execute(),调用resolveActivity()根据Intent匹配到对应的

    2024年02月11日
    浏览(38)
  • Android 四大组件之Activity详解

      最近在整理Android方面的知识,也算是对Android知识的一个复习总结。   Activity是Android组件中最基本也是最为常见用的四大组件之一,它提供一个可视化的用户界面,放置各种UI组件,与用户进行交互。一般来说,你所能看到界面都属于Activity。 右击包名——New——Acti

    2024年04月15日
    浏览(72)
  • Android复习(Android基础-四大组件)——Service与Activity通信

    我们前面学会了启动和停止服务的方法,但是服务虽然是在活动里启动的,但是启动服务之后,活动与服务之间基本没什么关系了。 正常情况,我们在Activity里调用startService()方法启动MyService这个服务,然后MyService的onCreate()和onStartCommand()方法就会得到执行。之后服务会一直处

    2024年02月13日
    浏览(41)
  • 【pygame学习+实战】第一篇:游戏最小系统

    14天学习训练营导师课程: 李宁《Python Pygame游戏开发入门与实战》 李宁《计算机视觉OpenCV Python项目实战》1 李宁《计算机视觉OpenCV Python项目实战》2 李宁《计算机视觉OpenCV Python项目实战》3 “我有一个梦想,那就是有生之年做出一款属于自己的游戏。” 不知道屏幕前的你是

    2023年04月19日
    浏览(48)
  • Vue入门简介【第一篇】

            🌴  1.1  什么是Vue      vue是一个构建用户界面UI的 渐进式javascript框架 ,渐进式的框架是指可以一步一步的由浅入深的去使用该框架。       vue官网: https://cn.vuejs.org/         🌴  1.2 Vue的 优点  ⭐️  1、体积小     压缩后33k左右,体积小意味着下载速度很快

    2024年02月03日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包