首页 > 其他分享 >LiveData的用法

LiveData的用法

时间:2023-08-10 12:11:05浏览次数:35  
标签:void ViewModel LiveData 用法 new 数据 页面

一.实时数据LiveData

  在上一节中,我们学习了ViewModel,了解到ViewModel的主要作用是存放页面所需要的各种数据。我们在示例代码中定义了接口,当数据发生变化的时候,采用接口的方式实现对页面的通知。但是这种方式是有缺陷的,当要存储的数据非常多的时候,就要定义大量的接口,代码会显得十分冗余,为此JetPack提供了LiveData组件。LiveData是一个可被观察的数据容器类,具体来说,可以将LiveData理解为一个数据的容器,它将数据包装起来,使数据成为被观察者,当数据发生变化的时候,观察者能够获得通知。我们不需要自己去实现观察者模式,LiveData内部已经默认实现好了。

  下面我们用LiveData替代上一节定义的接口,完成ViewModel和页面之间的通信。

二.LiveData和ViewModel的关系

  ViewModel用于存储页面所需要的数据,不仅如此,我们还可以在其中放一些与数据相关的业务逻辑。例如,可以在ViewModel中进行数据的获取和加工等操作。因此,ViewModel中的数据可能随着业务的变化而发生变化。对页面来说,它并不关心ViewModel的业务逻辑,它只关心需要展示的数据是什么,并且希望在数据发生变化的时候,能及时得到通知并做出更新。LiveData的作用就是,在ViewModel中的数据发生变化的时候通知页面更新。因此,LiveData通常被放在ViewModel中使用,用于包装ViewModel中那些需要被外界观察的数据。

三.LiveData的基本使用方法

  LiveData是一个抽象类,不能直接使用,通常使用的是他的直接子类MutableLiveData。下面我们改造上一节的代码,如下所示:

public class TimerViewModel extends ViewModel {
    private MutableLiveData<Integer> currentSecond;
    private Timer timer;
    private Integer second=0;
    @Override
    protected void onCleared() {
        super.onCleared();
        timer.cancel();
    }
    public void startTiming(){
        if(timer==null){
            timer=new Timer();
            TimerTask timerTask=new TimerTask() {
                @Override
                public void run() {
                    second++;
                    currentSecond.postValue(second);
                }
            };
            timer.schedule(timerTask,1000,1000);
        }
    }
    public LiveData<Integer> getCurrentSecond(){
        if(currentSecond==null){
            currentSecond=new MutableLiveData<>();
        }
        return currentSecond;
    }
}
public class MainActivity extends AppCompatActivity {
    private TextView tv_display;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iniComponent();
    }

    private void iniComponent() {
        tv_display=findViewById(R.id.tv_display);
        TimerViewModel timerViewModel=new ViewModelProvider(this).get(TimerViewModel.class);
        MutableLiveData<Integer> liveData= (MutableLiveData<Integer>) timerViewModel.getCurrentSecond();
        liveData.observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer second) {
                tv_display.setText(second+"");
            }
        });
        timerViewModel.startTiming();
    }
}

  在页面中,通过LiveData.observe()方法对LiveData所包装的数据进行观察,当该数据发生变化的时候,就可以得到更新后的数据,并在onChanged()方法中做出处理。当我们需要修改LiveData中的数据时,可以通过LiveData.postValue()和LiveData.setValue()方法来完成。postValue()方法用在非UI线程,setValue()方法用在UI线程中。

四.LiveData的原理

  为了更好地理解LiveData,我们可以深入LiveData.observe()方法的源码一探究竟。

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

  从源码可以看到,observe方法接收的第一个参数是LifecycleOwner对象,在本例中是Activity,第二个参数是一个Observer对象。从源码中可以发现,当页面的状态为Destroy时,直接return了,否则将observer添加为页面的观察者。也就是说,只有页面处于激活状态时,页面才可以收到来自LiveData的通知,若页面处于destroy状态,那么LiveData会自动清除与页面的关联,从而避免可能引起的内存泄漏问题。

五.LiveData.observeForever()方法

  LiveData还提供了一个名为observeForever()的方法,它的用法和observe方法相似,主要的区别在于,当LiveData中的数据发生变化时,无论页面处于什么状态,observeForever()方法都可以收到通知。因此,在用完之后,一定要记得调用removeObserver()方法来停止对LiveData的观察,否则LiveData会一直处于激活状态,Activity则永远不会被系统自动回收,这就造成了内存泄漏。

标签:void,ViewModel,LiveData,用法,new,数据,页面
From: https://www.cnblogs.com/luqman/p/LiveData.html

相关文章

  • 【Nginx用法】nginx location正则表达式写法,详解Nginx location 匹配规则(很详细哦)
    本文目录一、常用规则 二、实际使用建议三、Flag标志位四、If判断指令五、全局变量六、常用正则七、Rewrite规则八、Rewrite实例8.1实例一8.2实例二九、项目实例9.1项目一9.2项目实战作为一名Java开发人员,有些东西不经常使用,很容易忘记,好比nginx配置内容,以下内容是记录了公司......
  • Go语言中省略号用法大全
    Go语言中的ellipsis(即三个连续的点...)确实有几种用途。下面是这些用途的总结:定义变参函数(VariadicFunctions):Ellipsis用于函数定义时,表明该函数可以接受任意数量的参数。这些参数会被当作一个slice来处理。例如:funcsum(nums...int)int{total:=0for_,......
  • 【数据结构】bitset用法
    bitset用法bitset可以说是一个多位二进制数,每八位占用一个字节,因为支持基本的位运算,所以可用于状态压缩,n位bitset执行一次位运算的时间复杂度可视为n/32.输出只能用cout1.构造:inta=5;stringb="1011";charc[4]={'1','0','1','0'};bitset<10>s1(string("1001&qu......
  • VC用法汇总
    (1)如何通过代码获得应用程序主窗口的指针?主窗口的指针保存在CWinThread::m_pMainWnd中,调用AfxGetMainWnd实现。AfxGetMainWnd()->ShowWindow(SW_SHOWMAXMIZED)//使程序最大化.(2)确定应用程序的路径UseGetModuleFileName 获得应用程序的路径,然后去掉可执行文件名。Example......
  • 8.9-上午电机座的用法 电极座基准点不是整数的话需要用点改为整数
      ......
  • JavaScript用法
    JavaScript用法HTML中的JavaScript脚本代码必须位于<script>和</script>标签之间。Javascript脚本代码可被放置在HTML页面的<body>和<head>部分中。<script>标签<script>和</script>之间的代码包含了JavaScript<!DOCTYPEhtml><htmllang="en">......
  • Go语言中三个点的用法
    在Go语言中,三个点...在不同的上下文中有不同的含义。以下是一些常见的情况:可变参数(VariadicParameters):在函数定义中,...用于表示可变参数,允许函数接受不定数量的参数。这些参数被封装在一个切片中。funcfoo(values...int){//values是一个int切片for_,v......
  • c++中template的用法是什么?
    下文由ChatGPT生成在C++中,template是一种通用编程工具,用于创建通用的函数或类。通过使用模板,可以编写可以应用于不同数据类型的函数或类,从而实现代码的重用性和灵活性。template的使用方法如下:1.函数模板(FunctionTemplates)函数模板允许定义一个通用的函数,可以在不同数据类......
  • select的错误用法
     执行 manselect 会有如下提示 说明调用select函数时传入的timeval的值会变,所以在重复调用select的场景下,每次调用select,timeval的值都需要重新设置,如下是一个错误的示例,需要将设置时间值的代码移到while(1)里面 ......
  • merge into语句用法
    原文地址:https://zhuanlan.zhihu.com/p/47884584使用merge语句从一个或多个源中选择行以进行更新或插入表或视图。可以指定条件以确定是update还是insert目标表或视图。merge语句是组合多个操作的便捷方式。它可以让你避免多次使用INSERT,UPDATE和DELETE语句去操作数据。语法:m......