欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android最新架构之感受LiveData 与 ViewModel结合之美

程序员文章站 2022-06-07 16:53:54
...

来源:https://blog.csdn.net/qq_17766199

 

LiveData与ViewModel都是Android官方架构组件(Android Architecture Components)之一。

1.前言

虽说这篇是说LiveDataViewModel,但是或多或少都有涉及另外一个组件:Lifecycles 。它们连同Room都是在17年谷歌IO大会推出的,当时还是预览版,大致17年底时推出了正式版。到今年的IO大会过后,又增加了许多新成员。

Android最新架构之感受LiveData 与 ViewModel结合之美

可以看到27.0.0的v7库有依赖Lifecycles

Android最新架构之感受LiveData 与 ViewModel结合之美

当时Lifecycles有集成进SupportActivity

Android最新架构之感受LiveData 与 ViewModel结合之美

其实一开始我没有太当回事。。。直到27.1.0以后:

Android最新架构之感受LiveData 与 ViewModel结合之美

好吧,今天的主角出现了,LiveDataViewModel。看到这里我觉得是该了解一波了。

顺便看一下截止目前最新的v7:

Android最新架构之感受LiveData 与 ViewModel结合之美

发现好多常用的组件分离出了v4包,比如ViewPagerSwipeRefreshLayout,这里就不多说了。

2.优势

LiveData 是一个可以感知 Activity 、Fragment生命周期的数据容器。当 LiveData 所持有的数据改变时,它会通知相应的界面代码进行更新。同时,LiveData 持有界面代码 Lifecycle 的引用,这意味着它会在界面代码(LifecycleOwner)的生命周期处于 started 或 resumed 时作出相应更新,而在 LifecycleOwner 被销毁时停止更新。

上面的描述介绍了LiveData的优点:不用手动控制生命周期,不用担心内存泄露,数据变化时会收到通知。

ViewModel 将视图的数据和逻辑从具有生命周期特性的实体(如 Activity 和 Fragment)中剥离开来。直到关联的 Activity 或 Fragment 完全销毁时,ViewModel 才会随之消失,也就是说,即使在旋转屏幕导致 Fragment 被重新创建等事件中,视图数据依旧会被保留。ViewModels 不仅消除了常见的生命周期问题,而且可以帮助构建更为模块化、更方便测试的用户界面。

 

ViewModel的优点也很明显,为Activity 、Fragment存储数据,直到完全销毁。尤其是屏幕旋转的场景,常用的方法都是通过onSaveInstanceState()保存数据,再在onCreate()中恢复,真的是很麻烦。

其次因为ViewModel存储了数据,所以ViewModel可以在当前ActivityFragment中实现数据共享。

那么LiveDataViewModel的组合使用可以说是双剑合璧,而Lifecycles贯穿其中。

3.基本使用

这里我们按照官方Demo来简单说明,

1. 数据储存

Android最新架构之感受LiveData 与 ViewModel结合之美

LiveDataTimerViewModel很简单,在初始化时启动一个定时任务,每隔一秒通过postValue方法刷新一下数据。

Android最新架构之感受LiveData 与 ViewModel结合之美

Activity也很简单,创建一个观察者elapsedTimeObserver。当LiveDataTimerViewModel中数据有变化时,它就会接收到最新的数据。当然你的页面要处于STARTED 或者 RESUMED。除非你使用observeForever来观察数据,有兴趣的可以去查看源码来了解实现原理。

mLiveDataTimerViewModel.getElapsedTime().observeForever(elapsedTimeObserver);

2. Fragmnet 之间数据共享

 
  1. public class SeekBarViewModel extends ViewModel {

  2.  
  3.    public MutableLiveData<Integer> seekbarValue = new MutableLiveData<>();

  4. }

SeekBarViewModel中存储一个Integer类型的数据。

Android最新架构之感受LiveData 与 ViewModel结合之美

Android最新架构之感受LiveData 与 ViewModel结合之美

实现效果:

Android最新架构之感受LiveData 与 ViewModel结合之美

这个页面是上下各有一个Fragment_step5的Fragment,Fragment中各有一个SeekBar。效果是拖动其中的SeekBar,另一边的SeekBar也会随之一样变化。

4.简化使用

这里我写了一个小小的工具库Saber来处理(好吧,猝不及防的广告。。。),使用注解处理器(Annotation Processor)将繁琐的代码自动生成。

首先创建一个类,使用@LiveData注解标记你要保存的数据。注意这里的参数名称,下面会用到。

 
  1. public class SeekBar {

  2.    @LiveData

  3.    Integer value;

  4. }

Build – > Make Project 生成代码如下:

Android最新架构之感受LiveData 与 ViewModel结合之美

提供了ViewModel的常用操作。setXXX()要在主线程中调用,而postXXX()既可在主线程也可在子线程中调用。一般情况下可以直接使用。比如上面的Fragment例子。简化为:

Android最新架构之感受LiveData 与 ViewModel结合之美

Android最新架构之感受LiveData 与 ViewModel结合之美

默认使用@BindViewModel用于数据储存,如果需要Fragment之间数据共享,需要@BindViewModel(isShare = true),当然也要保证传入相同的key值。默认key值是类的规范名称,也就是包名加类名。

Android最新架构之感受LiveData 与 ViewModel结合之美

所以一旦需要互通的Fragment类名或包名不一致,就无法数据共享。这时可以指定key值:@BindViewModel(key = "value")

对于第一个例子我们可以这样使用:

 
  1. public class LiveDataTimerViewModel extends TimerViewModel {// <-- 继承生成的ViewModel

  2.  
  3.    private static final int ONE_SECOND = 1000;

  4.  
  5.    private long mInitialTime;

  6.  
  7.    public LiveDataTimerViewModel() {

  8.  
  9.        mInitialTime = SystemClock.elapsedRealtime();

  10.        Timer timer = new Timer();

  11.  
  12.        // Update the elapsed time every second.

  13.        timer.scheduleAtFixedRate(new TimerTask() {

  14.            @Override

  15.            public void run() {

  16.                final long newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000;

  17.                // setValue() cannot be called from a background thread so post to main thread.

  18.                postTime(newValue); //<--直接使用post方法。

  19.            }

  20.        }, ONE_SECOND, ONE_SECOND);

  21.  
  22.    }

  23.  
  24. }

Activity如下:

 
  1. public class ChronoActivity3 extends AppCompatActivity {

  2.  
  3.    private TextView textView;

  4.  
  5.    @BindViewModel

  6.    LiveDataTimerViewModel mTimerViewModel;

  7.  
  8.    @Override

  9.    protected void onCreate(Bundle savedInstanceState) {

  10.        super.onCreate(savedInstanceState);

  11.        setContentView(R.layout.activity_main);      

  12.        textView = this.findViewById(R.id.tv);

  13.        Saber.bind(this); // <-- 绑定

  14.    }

  15.  
  16.    @OnChange(model = "mTimerViewModel")

  17.    void setData(Long time){

  18.        String newText = MainActivity.this.getResources().getString(R.string.seconds, time);

  19.        textView.setText(newText);

  20.        Log.d("ChronoActivity3 ", "Updating timer");

  21.    }

  22. }

是不是使用起来更加的简洁了,如果一个页面有多个ViewModel可能效果更加的明显。

5.原理

因为实现大量借鉴了butterknife,所以使用方法与butterknife几乎一模一样。是不是想起了 butterknife 的@BindView 与 @OnClick

其实原理也不复杂,就是生成一个类来帮我们来获取ViewModel并实现数据的变化监听。如下:

 
  1. public class MainActivity_Providers implements UnBinder {

  2.  private MainActivity target;

  3.  
  4.  @UiThread

  5.  public MainActivity_Providers(MainActivity target) {

  6.    this.target = target;

  7.    init();

  8.  }

  9.  
  10.  private void init() {

  11.    target.mTimerViewModel = ViewModelProviders.of(target).get(LiveDataTimerViewModel.class);

  12.    target.mTimerViewModel.getTime().observe(target, new Observer<Long>() {

  13.      @Override

  14.      public void onChanged(Long value) {

  15.        target.setData(value);

  16.      }

  17.    });

  18.  }

  19.  
  20.  @CallSuper

  21.  @UiThread

  22.  public void unbind() {

  23.    MainActivity target = this.target;

  24.    if (target == null) {

  25.      throw new IllegalStateException("Bindings already cleared.");

  26.    }

  27.    this.target = null;

  28.  }

  29. }

 

相关标签: Android Viewmodel