Android——JetPack{ViewModel}

视图与数据模型之间的桥梁ViewModel

在页面功能较为简单的情况下,我们通常会将UI交互、数据获取等相关的业务逻辑全部写在页面中。但是在页面功能复杂的情况下,这样做就是不合适的。因为这样不符合“单一功能原则”。页面只应该负责处理用户与UI控件的交互,应该将UI与数据相关的业务逻辑隔离开
为了能够更好地将职能划分,Android为我们提供了ViewModel类,专门用于存放应用程序页面所需的数据


image.png

ViewModel的生命周期特性

由于Android在横竖屏切换,存在数据恢复的问题。现在ViewModel能为我们解决这个问题。ViewModel独立于配置变化。也就是说屏幕旋转所导致的Activity重建,并不会影响ViewModel的生命周期


ViewModel的生命周期

由图可以看到,ViewModel的生命周期是贯穿在Activity生命周期的始终的

ViewModel的基本使用方法

  1. build.gradle 添加依赖
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
  1. 写一个继承自ViewModel的类,TimerViewModel

public class TimeViewModel extends ViewModel {
    @Override
    protected void onCleared() {
        super.onCleared();
    }
}

ViewModel是一个抽象类,其中只有一个onCleared()方法。当ViewModel不再被需要,即与之相关的Activity都被销毁时,该方法会被系统调用

  1. 我们在TimeViewModel中创建一个计时器Timer,每隔1s,通过OnTimerChangeListener通知它的调用者
public class TimeViewModel extends ViewModel {
    private Timer timer;
    private int currendSecond;
    private OnTimeChangedListener timeChangedListener;

    public void setTimeChangedListener(OnTimeChangedListener timeChangedListener) {
        this.timeChangedListener = timeChangedListener;
    }

    public void startTiming(){
        if (timer == null) {
            timer = new Timer();
            TimerTask timerTask = new TimerTask() {
                @Override
                public void run() {
                    currendSecond++;
                    if (timeChangedListener != null) {
                        timeChangedListener.onTimeChanged(currendSecond);
                    }
                }
            };
        timer.schedule(timerTask,1000,1000);
        }
    }


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

    interface OnTimeChangedListener{
        void onTimeChanged(int second);
    }
}
  1. 在Activity中监听OnTimeChangeListener,并更新UI
    ViewModel的实例化过程,是通过ViewModelProvider来完成的,ViewModelProvider会判断ViewModel是否存在,弱存在则直接返回,若不存在则创建一个viewModel
public class TimerActivity extends AppCompatActivity {

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

    private void initComponent() {
        TextView timeTv = (TextView) findViewById(R.id.tv_second);
        //创建viewModel
        TimeViewModel timeViewModel = new ViewModelProvider(this).get(TimeViewModel.class);
        timeViewModel.setTimeChangedListener(new TimeViewModel.OnTimeChangedListener() {
            @Override
            public void onTimeChanged(int second) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        timeTv.setText("TIME:" + second);
                    }
                });
            }
        });
        timeViewModel.startTiming();
    }
}

运行并旋转屏幕,我们可以发现当旋转屏幕导致Activity重建时,计时器并没有停止。这意味着横竖屏状态下的viewModel是同一个,并没有被销毁

ViewModel的原理

我们通过new ViewModelProvider(ViewModelStoreOwner owner).get(Class clz)来创建ViewModel对象,而这里我们传入的this,指代当前的Activity。这是因为我们的Activity继承自ComponentActivity,而ComponentActivity默认实现了ViewModelStoreOwner接口

ComponentActivity

接口方法getViewModelStore()返回创建的ViewModelStore()
image.png

从ViewModelStore的源码可以看出,ViewModel实际上是以HashMap的形式被缓存起来了,ViewModel与页面之间没有直接的关联。通过ViewModelProvider进行关联。当页面需要ViewModel时,会向ViewModelProvider索要,ViewModelProvider会检查该ViewModel是否已经存在于缓存中,若存在则直接返回,若不存在,则实例化一个。因此,Activity由于配置变化导致的销毁重建并不会影响ViewModel,ViewModel是独立于页面存在的

ViewModel与AndroidViewModel

由于ViewModel是独立页面的生命周期存在的,所以在使用ViewModel时不要将任何类型的Context或者含有Context引用的对象传入ViewModel。这样会导致内存泄漏,但是如果希望在ViewModel中使用Context,可以使用AndroidViewModel 它继承自ViewModel,并接收Application作为Context,这意味着,它的生命周期和Application是一样的。这就不会造成内存泄漏了

ViewModel与onSaveInstanceState()方法

onSaveInstanceState()方法只能保存少量的、并且必须是支持序列化的数据,而ViewModel则没有这个限制。ViewModel能支持页面中所有的数据。

你可能感兴趣的:(Android——JetPack{ViewModel})