LiveData
LiveData是一种可观察的数据存储器类。与常规的可观察类不同,LiveData具有生命周期感知能力,意指它遵循其他应用组件(如Activity、Fragment或Service)的生命周期。这种感知能力可确保LiveData仅更新处于活跃生命周期状态的应用组件观察者。
优势:
- 确保界面符合数据状态
LiveData遵循观察者模式。当底层数据发生变化时,LiveData会通知Observer对象。您可以整合代码以在这些Observer对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。 - 不会发生内存泄漏
观察者会绑定到Lifecycle对象,并在其关联的生命周期遭到销毁后进行自我清理。 - 不会因Activity停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回栈中的Activity),则它不会接收任何LiveData事件。 - 不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。 - 数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity会在返回前台后立即接收最新的数据。 - 适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了Activity或Fragment,它会立即接收最新的可用数据。 - 共享资源
您可以使用单例模式扩展LiveData对象以封装系统服务,以便在应用中共享它们。LiveData对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察LiveData对象。
案例一:实现计时器
Activity
public class MainActivity extends AppCompatActivity {
private TextView textView;
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication()))
.get(MyViewModel.class);
textView.setText(String.valueOf(viewModel.getTime().getValue()));
viewModel.getTime().observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
textView.setText(String.valueOf(integer));
}
});
// 开启定时器
startTimer();
}
private void startTimer() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// 非 UI 线程用 postValue(),UI 线程用 setValue()
viewModel.getTime().postValue(viewModel.getTime().getValue() + 1);
}
}, 1000, 1000);
}
}
ViewModel
public class MyViewModel extends ViewModel {
private MutableLiveData<Integer> time;
public MutableLiveData<Integer> getTime() {
if (time == null) {
time = new MutableLiveData<>();
time.setValue(0);
}
return time;
}
}
案例二:同步不同Fragment中SeekBar的数据(Fragment之间的数据传递)
MainActivity 与 activity_main.xml
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="@+id/first"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="com.zc.myapp.FirstFragment" />
<fragment
android:id="@+id/second"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="com.zc.myapp.SecondFragment" />
</LinearLayout>
ViewModel
public class MyViewModel extends ViewModel {
private MutableLiveData<Integer> progress;
public MutableLiveData<Integer> getProgress() {
if (progress == null) {
progress = new MutableLiveData<>();
progress.setValue(0);
}
return progress;
}
}
FirstFragment 与 fragment_first.xml
public class FirstFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_first, container, false);
final SeekBar seekbar = root.findViewById(R.id.seekbar1);
final MyViewModel viewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MyViewModel.class);
viewModel.getProgress().observe(getActivity(), new Observer<Integer>() {
@Override
public void onChanged(Integer i) {
seekbar.setProgress(i);
}
});
seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
viewModel.getProgress().setValue(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return root;
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SeekBar
android:id="@+id/seekbar1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:min="0"
android:max="100"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp" />
</RelativeLayout>
SecondFragment 与 fragment_second.xml
public class SecondFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_second, container, false);
final SeekBar seekbar = root.findViewById(R.id.seekbar2);
final MyViewModel viewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MyViewModel.class);
viewModel.getProgress().observe(getActivity(), new Observer<Integer>() {
@Override
public void onChanged(Integer i) {
seekbar.setProgress(i);
}
});
seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
viewModel.getProgress().setValue(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return root;
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SeekBar
android:id="@+id/seekbar2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:min="0"
android:max="100"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp" />
</RelativeLayout>