ViewModel和LiveData的基本使用


在这里插入图片描述

ViewModel是干什么的?

在页面(活动/碎片)中,在很简单的情况在我们会将UI交互,数据的获得与处理等相关的逻辑,全都放在一个页面中,但是如果我们要处理的业务很复杂,这样做会显得代码冗杂且不利于解读,这样不符合"单一责任"原则.
所以页面就应该只是负责接收用户的交互以及数据的展示,其他逻辑应该放在另外一个东西上面,为此,Android为我们提供了ViewModel类,专门用来存放应用程序页面所需要的数据.它将页面中所需要的数据从页面中剥离出来.
在这里插入图片描述
另外,如果我们的应用程序支持横竖屏切换,当用户旋转手机屏幕时,我们还需要考虑数据的存储与恢复。如果数据不进行存储,那么通常我们还需要重新去获取一次。

而ViewModel能为我们解决这个问题,它独立于配置变化。也就是说,屏幕旋转导致的Activity重建,并不会影响到ViewModel的生命周期
在这里插入图片描述

LiveData是干什么的?

  1. 首先LiveData是一种可观察监听的数据存储类,当生命周期发生,数据有更新时,observer可感知监听到.

  2. 其次LiveData其实也是一个观察者模式的数据实体类,它可以跟它注册的观察者回调数据是否已经更新.

  3. LiveData还能知晓它绑定的Activity或者Fragment的生命周期,它只会给前台活动的activity回调(这个很厉害).这样你可以放心的在它的回调方法里直接将数据添加到View,而不用担心会不会报错.(你也可以不用费心费力判断Fragment是否还存活)

LiveData和ViewModel实现跨页面通信

官方建议LiveData和ViewModel一起使用,因为 ViewModel 支持共享作用域,并且官方文档都推荐了通过 共享 ViewModel 来实现跨页面通信的需求:
在这里插入图片描述
在这里我们使用LiveData和ViewModel实现一个小demo,ViewModel里面创建一个Timer计时器,每隔一秒,通过LiveData来更新主界面的UI,这种方法在只用之后我们可以清楚的看到,如果手机从横屏切换到竖屏,或者竖屏切换到横屏,它ViewModel里面Timer计时器,都不会重新开始计时.

1. 首先创建一个TimerViewModel类继承ViewModel

LiveData是一个数据的包装。具体的包装对象可以是任何数据,包括集合。它是一个抽象类,首先先创建一个类实现LiveData

public class TimerViewModel extends ViewModel
{
    
    
    private Timer timer;
    private int currentSecond;

    /**
     * LiveData是抽象类,MutableLiveData是具体实现类
     */
    private MutableLiveData<String> content;

    public MutableLiveData<String> getContent(){
    
    
        if(content == null){
    
    
            content = new MutableLiveData<>();
        }
        return content;
    }
    
    /**
     * 开始计时
     * */
    public void startTiming()
    {
    
    
        if (timer == null)
        {
    
    
            currentSecond = 0;
            timer = new Timer();
            TimerTask timerTask = new TimerTask()
            {
    
    
                @Override
                public void run()
                {
    
    
                    currentSecond++;
                    content.postValue(String.valueOf(currentSecond));
                }
            };
            timer.schedule(timerTask, 2000, 1000);//延迟2秒执行
        }
    }
    
    /**
     * 由于屏幕旋转导致的Activity重建,该方法不会被调用
     *
     * 只有ViewModel已经没有任何Activity与之有关联,系统则会调用该方法,你可以在此清理资源
     * */
    @Override
    protected void onCleared()
    {
    
    
        super.onCleared();
        timer.cancel();
    }
}

ViewModel是一个抽象类,其中只有一个方法onCleared(),当ViewModel不再被需要的时候,也就是与之相关的Activity都被销毁时,该方法会被系统调用,我们可以在这个方法里面执行一些资源释放的操作,以免内存泄漏。

既然ViewModel的销毁是由系统来判断和执行的,那么系统是如何判断的呢?是根据Context引用。因此,我们在使用ViewModel的时候,千万不能从外面传入Activity,Fragment或者View之类的含有Context引用的东西,否则系统会认为该ViewModel还在使用中,从而无法被系统销毁回收,导致内存泄漏的发生。

2. 在Activity或是fragment进行监听观察

public class MainActivity extends AppCompatActivity
{
    
    

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iniComponent();
    }

    private void iniComponent()
    {
    
    
        TextView tvTime = findViewById(R.id.tvTime);
        //构建了ViewModel对象
        TimerViewModel timerViewModel = new ViewModelProvider(this).get(TimerViewModel.class);

		//监听了content属性变化,只要触发了setValue/postValue方法就会走
        timerViewModel.getContent().observe(this, new Observer<String>() {
    
    
            @Override
            public void onChanged(String s) {
    
    
                tvTime.setText("TIME: " + s);
            }
        });
        timerViewModel.startTiming();
    }
}

首先创建一个 MutableLiveData(LiveData是抽象类)对象 ,通过 observe 方法可以订阅修改数据的通知,通过 postValue()或者 setValue() 方法发送事件更新数据,已经订阅的 Observer 能够得到数据更改的通知,就会回调 onChanged() 方法。

// 在UI线程中调用该方法通知数据变更
liveData.setValue(object);

// 在子线程中调用该方法通知数据变更,该方法中切换到UI线程后调用setValue方法
liveData.postValue(object);

总结

我们前面提到过,使用ViewModel的时候,不能将任何含有Context引用的对象传入ViewModel,因为这可能会导致内存泄露。但如果你希望在ViewModel中使用Context怎么办呢?我们可以使用AndroidViewModel类,它继承自ViewModel,并且接收Application作为Context,既然是Application作为Context,也就意味着,我们能够明确它的生命周期和Application是一样的,这就不算是一个内存泄露了。

使用ViewModel,不仅将界面和数据从代码上进行了分离,而且不再需要关心屏幕旋转带来的数据的丢失和获取问题。也许你会说onSaveInstanceState() 方法同样可以解决屏幕旋转带来的数据丢失问题,但它只能保存少量的能支持序列化的数据,而ViewModel没有这个限制,它能支持页面中所有的数据。但要注意的是,ViewModel不支持数据的持久化,当界面彻底销毁,ViewModel及其数据也就不存在了。

猜你喜欢

转载自blog.csdn.net/qq_65337539/article/details/128067870