Android Jetpack笔记 - ViewModel和LiveData

Android Jetpack笔记 - ViewModel和LiveData

这是Android Jetpack 系列文章的第一篇,主要是总结一下在学习Jetpack组件库时遇到的各种问题和学习心得。

为什么是Jetpack?

​ Google官方于17/18 IO大会上推出了Jetpack。Jetpack是一个组件库,包含了四大组件,以及四大组件下的很多小的模块。这些模块都是可以单独使用的,你可以直接引入其中一些成熟的模块来重构项目,这样就非常灵活。

Android Jetpack 的四大组件

这些组件包括:

Architecture Compinents(架构组件)
  • Data Bingding(数据绑定)

  • Room(数据库)

  • WorkManager(后台任务管家)

  • Lifecycle(生命周期)

  • Navigation(导航)

  • Paging(分页)

  • Data Binding(数据绑定)

  • LiveData(底层数据通知更改视图)

  • ViewModel(以注重生命周期的方式管理界面的相关数据)

Foundation(基础)
  • AppCompat(向后兼容)
  • Android KTX(编写更加简洁的Kotlin代码)
  • Multidex (多处理dex的问题)
  • Test(测试)
Behavior(行为)
  • Download manager(下载给管理器)

  • Media & playback(媒体和播放)

  • Notifications(通知)

  • Permissions(权限)

  • Preferences(偏好设置)

  • Sharing(共享)

  • Slices(切片)

UI(视觉交互)
  • Animation & transitions(动画和过渡)

  • Auto(Auto组件)

  • Emoji(标签)

  • Fragment(Fragment)

  • Layout(布局)

  • Palette(调色板)

  • TV(TV)

  • Wear OS by Google(穿戴设备)

ViewModel

今天我从ViewModel开始介绍JetPack组件。

1、生命周期

ViewModel是JetPack中用于实现UI数据持久化的一个模块。我们在开发的时候,经常会遇到一个问题,UI层的数据不好管理——因为有时Activity和Fragment的生命周期是不可控的。如果Activity销毁了,那数据就没了;又或者是旋转屏幕方向的时候数据的保存问题。虽然有 onSaveInstanceState() 这样的方法可以调用,但总归是不太灵活。

为此,ViewModel设计了特殊的生命周期:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0IYWxyF6-1573137872405)( https://upload-images.jianshu.io/upload_images/9764942-f2b3f9b162334ff0.png )]

如图所示,ViewModel的生命周期可以贯穿Activity的全过程,真正实现了对UI数据的妥善保存。

2、解决回调问题

在开发中我们对网络的请求大多是异步进行的,如果Activity被销毁了数据才回调,那很有可能造成空指针错误。我们可以把请求回来的数据保存在ViewModel中,这样就可以实现对数据的解耦,避免了这个问题。

3、Fragments间共享数据

同一个Activity下的不同Fragment之间经常需要共享数据,如果回调来回调去的,或者你持有我我持有你的实例,就会造成很多麻烦。ViewModel就很好的解决了这个问题。

4、栗子栗子!

下面我用一个具体的例子来介绍ViewModel的简单使用。这个demo的结构是一个Activity中有两个Fragment,分别是 FirstFragment 和 SecondFragment。demo很简单,两个 fragemnt 共享父 Activity 的 ViewModel ,都有一个 +1 的button,同时也都有一个跳转到另一个fragment的button。一个TextView负责显示当前计数器的数值。

Demo的界面

首先是ViewModelActivity,这是demo的主入口:

public class ViewModelActivity extends AppCompatActivity {

    private FragmentManager fragmentManager = getSupportFragmentManager();

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

    }

    @Override
    protected void onStart() {
        super.onStart();
        // 加载Fragment
        fragmentManager.beginTransaction().replace(R.id.fragment, new FirstFragment()).commit();

    }
}

FirstFragment:

public class FirstFragment extends Fragment {

    private TextView textView;
    private Button add;
    private Button jump;
    private MyViewModel myViewModel;


    public FirstFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //获取ViewModel实例
        myViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
        Log.d("model", String.valueOf(myViewModel));
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_first, container, false);
        add = view.findViewById(R.id.button_add);
        jump = view.findViewById(R.id.button_next);
        textView = view.findViewById(R.id.count_text);
        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //调用setCount方法加1
                myViewModel.setCount(1);
            }
        });

        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getFragmentManager().beginTransaction().replace(R.id.fragment, new SecondFragment()).commit();
            }
        });

        myViewModel.getCountLiveData().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                textView.setText(String.valueOf(integer));
            }
        });
    }
}

SecondFragment也是类似的:

public class SecondFragment extends Fragment {

    private TextView textView;
    private Button add;
    private Button jump;
    private MyViewModel myViewModel;

    public SecondFragment() {
        // Required empty public constructor
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_second, container, false);
        add = view.findViewById(R.id.button_add);
        jump = view.findViewById(R.id.button_next);
        textView = view.findViewById(R.id.count_text);
        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myViewModel.setCount(1);
            }
        });

        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getFragmentManager().beginTransaction().replace(R.id.fragment, new FirstFragment()).commit();
            }
        });

        myViewModel.getCountLiveData().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                textView.setText(String.valueOf(integer));
            }
        });
    }

获取ViewModel实例那里,使用 ViewModelProviders这个命令可以获取一个 ViewModel 实例,这个实例是对应于 Activity 的,对于一个ViewModel Class 会有唯一的一个实例。在这个Activity的生命周期中,这个ViewModel会一直存活。

ViewModel的类:

/**
 * Created By Diao Su
 * Date 2019/11/4
 */
public class MyViewModel extends ViewModel {
    private int VOLUME = 0;
    private final MutableLiveData<Integer> countLiveData = new MutableLiveData<>();

    public void setCount(int count) {
        countLiveData.postValue(VOLUME += count);
    }

    public MutableLiveData<Integer> getCountLiveData() {
        return countLiveData;
    }
}

可以看到,这个MyViewModel是继承自ViewModel的,定义一个int型变量 VOLUME 来存放目前计数器的值,新建一个Integer型的LiveData来存放VOLUME。一般来说,LiveData是配合着ViewModel使用的,其构造函数的泛型决定了LiveData能存放什么数据。

此外,我们还定义了setCountgetCountLiveData两个方法,用以设置VOLUME和取出存放VOLUME的LiveData。那么为什么不能直接取出VOLUME而是要返回一个LiveData呢?读到后面你就知道了~

注意以下这段fragment中的代码:

 myViewModel.getCountLiveData().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                textView.setText(String.valueOf(integer));
            }
        });

这里利用了viewmodel封装好的一个方法,实现了观察者模式。我们不用在意实现,就能很方便的在onChanged回调函数里取出当前最新的VOLUME值。这也符合JetPack的设计初衷之一 —— 就是我们不用去关心很多组件的实现或者生命周期balabala,我们只用去关注怎么使用JetPack给我们提供的便利完成高质量的项目。

运行的效果是,两个fragment就像是在同一个界面中一样,可以同时操作计数器:

以上就是JetPack系列的第一期啦~用好ViewModel+LiveData可以给我们的项目开发带来非常多的便利。下一期我将继续学习Room组件。

你可能感兴趣的:(Android,JetPack,安卓)