1.LiveData介绍

LiveData是一个可被调查的数据容器类,它可以包含任何类型的数据。具体来说,可以将LiveData理解为一个数据的容器,它将数据包装起来,使数据成为调查者,当该数据产生改变时,调查者可以取得告诉。与常规的可调查类不同,LiveData可以感知(如Activity,Fragment或Service)的生命周期。

简略来说,LiveData具有如下优势

  1. LiveData遵从调查者模式。当生命周期状况产生改变时,LiveData会告诉Observer目标,可以在这些Observer目标中更新界面。

  2. 不会内存泄漏

  3. 假如调查者的生命周期处于非活泼状况(如返回栈中的Activity),则它不会接纳任何LiveData事情,但是,当非活泼状况变成活泼状况时会马上接纳最新的数据(后台的Activity返回前台时)

  4. 当config导致Activity/Fragment重建时,不需求再手动的办理数据的存储与康复。

2.LiveData和ViewModel的关系

ViewModel用于存放页面所需求的各种数据,关于页面来说,它并不关怀ViewModel中的业务逻辑,它只关怀需求展现的数据是什么,而且希望在数据产生改变时,可以及时得到告诉并作出更新。

LiveData的作用便是,在ViewModel中的数据产生改变时告诉页面,用于包装ViewModel中哪些需求被外界调查的数据。

JetPack-(5)-LiveData 可被调查的数据容器类

3.LiveData运用办法

首先,在ViewModel视图模型中定义LiveData数据,如 MutableLiveData

JetPack-(5)-LiveData 可被调查的数据容器类

在该类中供给了postValue和setValue两个函数

JetPack-(5)-LiveData 可被调查的数据容器类

  • setValue() 只能在主线程中调用给LiveData设置数据
  • postValue()用于在非主线程中给LiveData设置数据

然后,在Activity组件中,调用LiveData参数的observer办法,增加数据改变监听器 androidx.lifecycle.Observer,一旦LiveData数据产生了改动,就会回调Observer监听器中的onChanged函数

JetPack-(5)-LiveData 可被调查的数据容器类

3.ViewModel+LiveData简略运用

3.1 在ViewModel中

首先在ViewModel中定义LiveData数据,因为LiveData是一个抽象类,不能直接运用它,所以咱们通常运用的都是它的直接子类MutableLiveData,然后定义了一个Integer类型的值,当该值产生改动时,会触发LiveData设置的Observer监听器。

JetPack-(5)-LiveData 可被调查的数据容器类

3.2 在Activity中

在Activity体系组件中,绑定ViewModel,从ViewModel中获取LiveData显现到UI界面中,并为该LiveData设置Observer监听器,监听LiveData的数据改变 发动Timer定时器,修改ViewModel中的LiveData数据,在LiveData数据产生改动时,会自动回调Observer监听器的onChanged办法


public class MainActivity extends AppCompatActivity {
    private MyViewModel myViewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = findViewById(R.id.textview);
        myViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()))
                .get(MyViewModel.class);
        //将ViewModel中的数据设置到视图View组件中
        textView.setText(String.valueOf(myViewModel.getSecond().getValue()));
        //设置LiveData监听
        myViewModel.getSecond().observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                //将ViewModel中的数据设置到视图View组件中
                textView.setText(String.valueOf(integer));
            }
        });
        startTimer();
    }
    public void startTimer() {
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                //获取ViewModel中的数据
                Integer value = myViewModel.getSecond().getValue();
                //将ViewModel中的数据自增1
                myViewModel.getSecond().postValue(value + 1);
            }
        }, 1000, 1000);
    }
}

3.3 运转作用:

应用发动后,在界面中发动定时器,对ViewModel中的LiveData数据进行累加,LiveData设置了Observer监听,数据改动时回调Observer的onChanged办法更新UI显现。就算切换屏幕反向,也不会影响数据累加显现

竖屏状况下

JetPack-(5)-LiveData 可被调查的数据容器类

切换横屏数据依然存在

JetPack-(5)-LiveData 可被调查的数据容器类

4.ViewModel+LiveData+Fragment运用

在Activity体系组件中设置两个Fragment,两个Fragment之间通过ViewModel+LiveData进行通讯

在其中一个Fragment中设置SeekBar拖动条,将数值设置到别的一个Fragment中的TextView中显现

4.1 ViewModel + LiveData代码

自定义ViewModel子类承继ViewModel,在ViewModel中,定义LiveData类型的数据,此处挑选运用MutableLiveData< Integer >数据类型,当该值产生改变的时分,会触发LiveData设置的Observer监听器

JetPack-(5)-LiveData 可被调查的数据容器类

4.2 Activity组件代码

在该Activity组件中,维护了两个Fragment,两个Fragment之间凭借ViewModel+LiveData进行通讯

JetPack-(5)-LiveData 可被调查的数据容器类

布局文件: 在Activity中设置了两个Fragment,他们之间凭借ViewModel+LiveData进行通讯

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".HomeActivity">
   <androidx.constraintlayout.widget.Guideline
       android:id="@+id/guideline"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:orientation="horizontal"
       app:layout_constraintGuide_percent="0.5" />
   <androidx.fragment.app.FragmentContainerView
       android:id="@+id/fragmentContainerView1"
       android:name="com.wzj.livedata.Fragment1"
       android:layout_width="0dp"
       android:layout_height="0dp"
       app:layout_constraintBottom_toTopOf="@+id/guideline"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
   <androidx.fragment.app.FragmentContainerView
       android:id="@+id/fragmentContainerView2"
       android:name="com.wzj.livedata.Fragment2"
       android:layout_width="0dp"
       android:layout_height="0dp"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="@+id/guideline" />
</androidx.constraintlayout.widget.ConstraintLayout>

4.3 Fragment代码

第一个Fragment布局

Fragment中创建了一个SeekBar拖动条组件

JetPack-(5)-LiveData 可被调查的数据容器类

第一个Fragment代码

先将ViewModel中的LiveData数据中的进展值给SeekBar,意图是为了在屏幕旋转的时分,可以随时康复数据

然后在SeekBar的拖动数据中,修改ViewModel中的LiveData数据 当数据修改时,对应的Fragment2中的TextView会改写显现新的数据

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_1, container, false);
        //获取拖动条
        SeekBar seekBar = view.findViewById(R.id.seekBar);
        //获取ViewModel
        HomeViewModel homeViewModel = new ViewModelProvider(requireActivity(),  ViewModelProvider.AndroidViewModelFactory.getInstance(requireActivity().getApplication()))
                .get(HomeViewModel.class);
        seekBar.setProgress(homeViewModel.getProgress().getValue());
        //设置进展条拖动事情
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                homeViewModel.getProgress().setValue(i);
            }
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
        return view;
    }
}

第二个Fragment布局

Fragment2中只要一个TextView

JetPack-(5)-LiveData 可被调查的数据容器类

第二个Fragment代码

在 Fragment2 中 , 只放了一个 TextView 组件 , 该组件显现的是 ViewModel 中的 LiveData 数据 , 当该 LiveData 数据产生改动时 , 对应 TextView 显现也随之更新 ;

public class Fragment2 extends Fragment {
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
       // Inflate the layout for this fragment
       View view = inflater.inflate(R.layout.fragment_2, container, false);
       //获取文本
       TextView textView = view.findViewById(R.id.textViews);
       //获取ViewModel
       HomeViewModel homeViewModel = new ViewModelProvider(requireActivity(), ViewModelProvider.AndroidViewModelFactory.getInstance(requireActivity().getApplication())).get(HomeViewModel.class);
       //设置数据
       textView.setText(String.valueOf(homeViewModel.getProgress().getValue()));
       homeViewModel.getProgress().observe(requireActivity(), new Observer<Integer>() {
           @Override
           public void onChanged(Integer integer) {
               textView.setText(String.valueOf(integer));
           }
       });
       return view;
   }
}

5.Map和switchMap

5.1 Map

map函数首要的功用便是依据原LiveData,对其原LiveData的值进行改动然后生成一个新的LiveData。依据原LiveData。新的LiveData的值必须依据旧的LiveData中的值

Transformations.map函数的作用是创建一个新的LiveData目标,这个新的LiveData目标的值是原始LiveData目标的值通过函数处理后的成果。这个函数接纳两个参数,一个LiveData目标和一个函数.这个函数将被应用到LiveData目标的每一个值上

举例说明:

在ViewModel中运用 Transformations.map将原LiveData进行更改

首先咱们定义一个实体类:

这便是一个一般的实体类,没什么好说的

public class User {
    private String firstName;
    private String lastName;
    private int age;
    public User(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

然后定义ViewModel


public class UserViewModel extends ViewModel {
   private MutableLiveData<User> userLiveData;
   private LiveData<String> userName;
   private int countReserved;
   public UserViewModel() {
       this.countReserved = countReserved;
       this.userLiveData = new MutableLiveData<>();
       //运用转换函数map,创建一个新的LiveData目标
       this.userName = Transformations.map(userLiveData, user -> user.getFirstName() + " " + user.getLastName());
   }
   public MutableLiveData<User> getUserLiveData() {
       return userLiveData;
   }
   public void setUserLiveData(MutableLiveData<User> userLiveData) {
       this.userLiveData = userLiveData;
   }
   public LiveData<String> getUserName() {
       return userName;
   }
   public void setUserName(LiveData<String> userName) {
       this.userName = userName;
   }
   public int getCountReserved() {
       return countReserved;
   }
   public void setCountReserved(int countReserved) {
       this.countReserved = countReserved;
   }
}

注意看第11行的代码,

this.userName = Transformations.map(userLiveData, user -> user.getFirstName() + " " + user.getLastName());

这段代码的作用是创建一个新的LiveData目标userName,它的值是userLiveData中User目标的firstName和lastName,每当userLiveData的值产生改变时,userName的值也会相应的更新

在Activity中

public class UserActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);
        TextView tv_user = findViewById(R.id.tv_user);
        Button btn_user = findViewById(R.id.btn_user);
        UserViewModel userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
        btn_user.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                User user = new User("姓名1", "姓名2", 888);
                //给ViewModel设置数据
                Toast.makeText(UserActivity.this, user.getFirstName() +" "+ user.getLastName() +" "+ user.getAge(), Toast.LENGTH_SHORT).show();
                userViewModel.getUserLiveData().postValue(user);
            }
        });
        userViewModel.getUserName().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                tv_user.setText(userViewModel.getUserName().getValue());
            }
        });
    }
}

在Activity中,咱们给User设置的值是两个姓名加年纪参数,然后在显现的时分,咱们用的是UserName的值 而不必本来的UserLiveData,所以打印出来的时分,应该只要两个姓名 而没有了年纪这个参数

JetPack-(5)-LiveData 可被调查的数据容器类

5.2 switchMap

switchMap是依据传入的LiveData的值,然后判断这个值,然后再去切换或许构建新的LiveData。手动生成新的LiveData

6.总结

LiveData之所以可以成为Activity与ViewModel之间通讯的桥梁,而且还不会有内存泄漏的危险,靠的便是Lifecycles组件。LiveData在内部运用了Lifecycles组件来自我感知生命周期的改变,从而可以在Activity毁掉的时分及时释放引用,避免产生内存泄漏的问题。

别的,因为要减少性能消耗,当Activity处于不行见状况的时分(比方手机息屏,或许被其他Activity遮挡),假如LiveData中的数据产生了改变,是不会告诉给调查者的。只要当Activity从头康复可见状况时,才会将数据告诉给调查者。而LiveData之所以可以完成这些细节的优化,依托的还是Lifecycles组件。

还有一个小细节,假如在Activity处于不行见状况的时分,LiveData产生了多次数据改变,当Activity康复可见状况时,只要最新的那份数据状况才会告诉给调查者,前面的数据在这种情况下相当于已通过期了,会被直接丢弃。