前言
在上一節中,我們學習了ViewModel,我們使用的是介面來完成ViewModel與頁面之間的通信,其實這并不是好的方案, 這篇博客,就讓我們來了解LiveData與ViewModel是如何配合作業的,
LiveData介紹
LivaData是一個可被觀察的資料容器類,具體來說,可以將LiveData理解為一個資料的容器,它將資料包裝起來,使資料成為觀察者,當該資料發生變化時,觀察者能夠獲得通知,與常規的可觀察類不同,LiveData可以感知(如Activity、Fragment或Service)的生命周期,
簡單來說,LiveData具有如下優勢
- LiveData 遵循觀察者模式,當生命周期狀態發生變化時,LiveData 會通知 Observer 物件,可以在這些Observer物件中更新界面
- 不會發送記憶體泄露
- 如果觀察者的生周期處于非活躍狀態(如回傳堆疊中的Activity),則它不會接收任何LivaData事件,但是,當非活躍狀態變成活躍狀態時會立刻接收最新的資料(后臺的Activity回傳前臺時)
- 當config導致Activity/Fragment重建時,不需要再手動的管理資料的存盤與恢復,
LiveData和ViewModel的關系
ViewModel用于存放頁面所需要的各種資料,對頁面來說,它并不關心ViewModel中的業務邏輯,它只關心需要展示的資料是什么,并且希望再資料發送變化時,能及時得到通知并做出更新,LiveData的作用就是,在ViewModel中的資料發生變化時通知頁面,用于包裝ViewModel中那些需要被外界觀察的資料,
LiveData基本使用
在上篇博客中的ViewModel的計時器案例的基礎上,我們使用LiveData對介面進行改寫
1.LiveData是一個抽象類,不能直接使用,我們通常使用的是它的直接子類MutableLiveData,代碼如下
public class LiveDataViewModel extends ViewModel {
private MutableLiveData<Integer> currentSecond;
private Timer timer;
private int current;
@Override
protected void onCleared() {
super.onCleared();
//釋放資源
timer.cancel();
}
public LiveData<Integer> getCurrentSecond(){
if(currentSecond == null){
currentSecond = new MutableLiveData<>();
}
return currentSecond;
}
//開始定時器
public void startTiming(){
if(timer == null){
current = 0;
timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
if(currentSecond!=null){
currentSecond.postValue(current++);
}
}
};
timer.schedule(timerTask,1000,1000);
}
}
//關閉定時器
public void stopTiming(){
timer.cancel();
}
}
當開始定時器的時候,也就是我們資料資源發生變化的時候,我們需要呼叫livedata.postvalue方法,通知頁面我們資料源已經發生了改變,至于為什么不用livedata.setValue方法,等下我們會說到,
2.接著我們在Activity中創建ViewModel,并監聽ViewModel里面currentSecond資料的變化,
public class LiveDataActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data);
iniComponent();
}
private void iniComponent() {
//通過ViewModelProvider得到ViewModel
final LiveDataViewModel viewModel = new ViewModelProvider(this).get(LiveDataViewModel.class);
//得到ViewModel中的LiveData
final MutableLiveData<Integer> liveData = (MutableLiveData<Integer>) viewModel.getCurrentSecond();
//通過liveData.observer()觀察ViewModel中資料的變化
liveData.observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
//收到回呼后更新UI界面
TextView tv = findViewById(R.id.tv_texts);
tv.setText("小鑫啊"+integer);
}
});
//關閉定時器
findViewById(R.id.btnReset).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//通過LiveData.setValue()/LiveData.postValue()
//完成對ViewModel中資料的更新
liveData.setValue(0);
//關閉定時器
viewModel.stopTiming();
}
});
//計時開始
viewModel.startTiming();
}
}
在頁面中,通過LiveData.observe()方法對LivaData所包裝的資料進行觀察,當我們資料源發生變化了(也就是我們想修改LivaData所包裝的資料時),就可以通過LiveData.postValue/LiveData.setValue()來完成,然后onChanged方法就會收到我們修改之后的資料,我們就可以對UI進行更改了.
需要注意的是:postValue()方法用在非UI執行緒中,而setValue()方法用在UI執行緒中,這就是為什么我們在開始定時器的時候,需要呼叫postVaule()發送資料了(因為定時器是運行在非UI執行緒的).

運行結果如下:

LivaData的基本使用就到這里,是不是很簡單啊! 接下來,就讓我們來探討下LiveData的原理吧!!!
LiveData的原理
我們知道LiveData是通過觀察者模式實作的,當資料發送改變的時候,會回呼Observer的onChanged(),接下來就讓我們深入Observer方法的原始碼一探究竟
observe原始碼
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//判斷當前wapper已經添加過,如果添加過就直接回傳,否則回傳null
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
//如果已經添加過,就直接回傳
if (existing != null) {
return;
}
//沒有添加過,則添加wrapper
owner.getLifecycle().addObserver(wrapper);
}
從原始碼可以看出,Observer()方法接收的第一個引數是一個LifecleOwner物件,我們傳入的是this,因為this的祖父類實作了這個介面,也正是LifecleOwner物件,LiveData才會具體生命周期感知能力,
首先, 通過owner.getLifecycle().getCurrentState()獲取當前頁面的狀態,如果當前頁面被銷毀了,就直接回傳,也就是說LiveData會自動清除與頁面的關聯,
LifecycleBoundObserver 原始碼
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
當呼叫 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer),本質是通過 ObserverWrapper將observer包裝起來,得以LiveData能對生命周期狀態得以進行監聽,是通過onStateChanged和shouldBeActive方法
- shouldBeActive 這里呼叫LiftCycle的方法,表達如果當前生命周期的狀態為onStart,onResume,onPause時 回傳true,也就是說只有這三個狀態可以接收資料更新,
- onStateChanged 是LifecycleEventObserver介面的方法,當生命周期發送變化的時候會回呼它,如果當前生命周期狀態是destory,就會直接移除觀察者,否則就會呼叫activeStateChanged(shouldBeActive());方法激活觀察者.
方法中的最后一行代碼將observer與Activity的生命周期關聯在一起,因此,LivaData能夠感知頁面的生命周期,
observer方法小結
- 判斷是否已經銷毀,如果當前頁面銷毀,LiveData自動清除與頁面的關聯
- 用LifecycleBoundObserver 對observer進行一個包裝
- 判斷當前observer是否已經添加過,添加過就直接回傳
- 將observer方法與Activity的生命周期進行關聯
setValue方法
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
setValue()中,首先 斷言是主執行緒,這里的關鍵是dispatchingValue(null)方法
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
只有處于active(激活)狀態的觀察者,這個方法就會把資料發送給它們,由于每次dispathchingValue傳入的null,所以會走else這一部分代碼, 這時候就會遍歷所有的observer,最后通過呼叫considerNotify()將資料進行分發給所有的observer
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
//如果當前observer不是激活狀態,也就是當前頁面被destory,直接return.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
只有出于活躍狀態且資料是資料是最新的,才會去分發資料,最后回呼到我們熟悉的onChanged()方法,
postValue方法
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
postValue方法是可以在子執行緒(非UI執行緒)發送資料的,但是onChanged()方法始終是在主執行緒? 答案就在postToMainThread(mPostValueRunnable)方法中;
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
創建一個Handler將子執行緒中的任務發送到主執行緒去執行,其本質還是呼叫了setValue()方法
LiveData.observeForever()方法
如果你想無論頁面處于何種生命周期,setValue/postValue之后立刻回到資料,那么可以使用observerForever()方法,使用起來與observer()沒有太大差別. 因為AlwaysActiveObserver沒有實作GenericLifecycleObserver 介面,不能感應生命周期,
但是需要注意的是,在用完之后,一定要記得在onDestroy()方法中呼叫removeObserver()方法來停止對LiveData的觀察,否則LiveData會一直處于激活狀態,Activity則永遠不會被系統自動回收,會造成記憶體泄露,
ViewModel+LiveData實作Fragment間的通信
我們已經知道,ViewModel能夠將資料從Activity中剝離出來,只要Activity不被銷毀,ViewModel會一直存盤,并且獨立于Activity的配置變化,
Fragment可以被看作Activty的子頁面,即一個Activity中可以包含多個Fragment.這些Fragment彼此獨立,但是又都屬于同一個Activity.
基于ViewModel和Fragment組件的這些特性,我們可以利用LiveData,實作同一個Activity中的不同Fragment間的通信,因為不同的Fragment得到的都是同一個LiveData;

定義ViewModel和LiveData
public class SharedViewModel extends ViewModel {
private MutableLiveData<String> content;
@Override
protected void onCleared() {
super.onCleared();
//釋放資源
content= null;
}
public LiveData<String> getContent(){
if(content == null){
content = new MutableLiveData<>();
}
return content;
}
}
初始化Fragment
Fragment之間的跳轉我們使用導航圖來進行跳轉
share_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/share_graph"
app:startDestination="@id/masterFragment">
<fragment
android:id="@+id/masterFragment"
android:name="com.example.jetpack.MasterFragment"
android:label="fragment_master"
tools:layout="@layout/fragment_master" >
<action
android:id="@+id/action_masterFragment_to_detailFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/detailFragment"
android:name="com.example.jetpack.DetailFragment"
android:label="fragment_detail"
tools:layout="@layout/fragment_detail" />
</navigation>
MasterFragment 布局
我們使用EditText輸入框,輸入內容后,點擊跳轉到DetailFragment后,DetailFragment獲取到輸入框的內容,并顯示在TextView上
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MasterFragment"
android:gravity="center"
android:orientation="vertical">
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="輸入內容"
android:textSize="20sp"/>
<Button
android:id="@+id/toDetail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="toDetailFragment"/>
</LinearLayout>
MasterFragment 代碼
private SharedViewModel model;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_master, container, false);
//設定資料
model = new ViewModelProvider(getActivity()).get(SharedViewModel.class);
final MutableLiveData<String> mutableLiveData = (MutableLiveData<String>) model.getContent();
final EditText editText = view.findViewById(R.id.edit_text);
view.findViewById(R.id.toDetail).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//獲取EditText中的資料,并通知livaData進行更新
String text = editText.getText().toString().trim();
mutableLiveData.setValue(text);
Navigation.findNavController(v).navigate(R.id.action_masterFragment_to_detailFragment);
}
});
return view;
}
detailFragment布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DetailFragment"
android:gravity="center">
<TextView
android:id="@+id/tv_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="哈哈哈"
android:textSize="30sp"
android:textStyle="bold"/>
</LinearLayout>
detailFragment代碼
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view =inflater.inflate(R.layout.fragment_detail, container, false);
final TextView textView = view.findViewById(R.id.tv_text1);
SharedViewModel model = new ViewModelProvider(getActivity()).get(SharedViewModel.class);
MutableLiveData<String> mutableLiveData = (MutableLiveData<String>) model.getContent();
//對LiveData進行監聽
mutableLiveData.observe(getActivity(), new Observer<String>() {
@Override
public void onChanged(String s) {
//顯示在UI上
textView.setText(s);
}
});
return view;
}
Activity布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".viewmodel.ShareActivity"
android:gravity="center">
<fragment
android:id="@+id/nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost= "true"
app:navGraph="@navigation/share_graph"/>
</LinearLayout>
只是定義一個Fragment來顯示兩個Fragment而已,代碼檔案沒有進行任何的更改
運行程式:

事實證明,兩個Fragment獲取到的是同一個LiveData, 在MasterFragment對LiveData資料進行更改,在DetailFragment對LiveData進行監聽,并將監聽到的資料顯示在TextView上面, 是不是也非常簡單啦
總結
- 本節中,我們學習了LiveData+ViewModel的基本使用,
- 并對LiveData原始碼,進行了一個大概的分析,知道了LiveData為什么能感知組件的生命周期
- LiveData的本質是觀察者模式,可以感知頁面的生命周期,當然你也可以使用observeForver()方法讓LiveData忽略頁面的生命周期,但是需要注意,用完之后要在onDestroy()方法用removeObserver()方法移除監聽,否則會造成記憶體泄露,
- LiveData大部分是在ViewModel中使用的,但是它的作用不止于此,下節,我們就來說,LiveData如果搭配Room資料庫組件進行使用.
好了,LiveData到這里就結束了,不足之處,望大家指出來,謝謝,
參考:
- LiveData決議
- LiveData原始碼決議
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/208836.html
標籤:其他
