LiveData概觀
[LiveData](https://developer。android。google。cn/reference/android/arch/lifecycle/LiveData。html)是一個可觀測的資料保持類。與常規觀察不同,LiveData是能感知生命週期的,這意味著它尊重其他應用程式元件(如activity、fragment或service)的生命週期。這種意識確保LiveData只更新處於活躍生命週期狀態的應用程式元件觀察者。
依賴方式:
```
dependencies {
def lifecycle_version = “1。1。1”
// ViewModel and LiveData
implementation “android。arch。lifecycle:extensions:$lifecycle_version”
// alternately - if using Java8, use the following instead of compiler
implementation “android。arch。lifecycle:common-java8:$lifecycle_version”
}
kotlin的依賴方式請見:[kotlin依賴方式](https://developer。android。google。cn/topic/libraries/architecture/adding-components#lifecycle)
LiveData關注的觀察者是處於活躍狀態的,對於觀察了資料但是沒有處於活躍狀態的元件,是不會收到資料更新的。
你可以在實現了[LifecycleOwner](https://developer。android。google。cn/reference/android/arch/lifecycle/LifecycleOwner。html)介面的類中註冊一個觀察者,這種關係允許你在activity/fragment的生命週期走到Destroy時把觀察者移除。
這對於activity和fragment特別有用,因為它們可以安全地觀察LiveData物件,並且不擔心洩漏,activity和fragment在它們的生命週期被破壞時立即被取消訂閱。
空說無憑,我們直接看看原始碼是怎麼回事。
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer
if (owner。getLifecycle()。getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
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) {
owner。getLifecycle()。addObserver(wrapper);
}
從以上可以看出,當我們呼叫`observe`方法時,會傳入一個`LifecycleOwner`物件,在前一篇文章[Lifecycles](https://www。jianshu。com/p/77b362256101)中我們瞭解到,activity/fragment在Support Library 26。1。0以及以後的版本已經實現了LifecycleOwner介面,所以這裡其實就相當於activity的上下文引用。注意觀察第二行程式碼,當我們獲取到當前元件處於destroy狀態時,就結束了方法。
我們先不管中間的程式碼,我們看最後一行,是不是感覺很熟悉,我們的LiveData裡的確有一個觀察者`LifecycleBoundObserver`,來觀察了activity等元件的生命週期。
我們一步步往下走,這個`LifecycleBoundObserver`裡面又是啥樣呢?還是一樣,我們從原始碼一步步分析。
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
@NonNull final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer
super(observer);
mOwner = owner;
@Override
boolean shouldBeActive() {
return mOwner。getLifecycle()。getCurrentState()。isAtLeast(STARTED);
public void onStateChanged(LifecycleOwner source, Lifecycle。Event event) {
if (mOwner。getLifecycle()。getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
void detachObserver() {
mOwner。getLifecycle()。removeObserver(this);
我們看到`LifecycleBoundObserver`繼承了`ObserverWrapper`並且實現了`GenericLifecycleObserver`介面。到這裡,我們心裡想的是不是每一個強大的工具或者強大的類的產生都不容易,設計到方方面面。廢話不多說,我們繼續看看google裡大神的程式碼。
我們首先看`shouldBeActive()`這個方法,它返回的是當前元件是否是活躍狀態。
**在這裡要多說一句,什麼是Lifecycle的活躍狀態呢?
我們看程式碼`mOwner。getLifecycle()。getCurrentState()。isAtLeast(STARTED)`這個方法告訴我們活躍狀態在Lifecycle中是狀態至少是`STARTED`。在Lifecycle中有兩個列舉STATE / EVENT。其中event是對應於activity等的生命週期,而state是Lifecycle自己的狀態,我們可以看看原始碼**
/**state你可以看作一個個節點,event就在這些節點的邊緣。
* Lifecycle states。 You can consider the states as the nodes in a graph and
* {@link Event}s as the edges between these nodes。
*/
public enum State {
/**在這個狀態,lifecycle不會發布任何事件。該狀態在activity destroy之前發生
* Destroyed state for a LifecycleOwner。 After this event, this Lifecycle will not dispatch
* any more events。 For instance, for an {@link android。app。Activity}, this state is reached
* right before Activity‘s {@link android。app。Activity#onDestroy() onDestroy} call。
*/
DESTROYED,
/**
* Initialized state for a LifecycleOwner。 For an {@link android。app。Activity}, this is
* the state when it is constructed but has not received
* {@link android。app。Activity#onCreate(android。os。Bundle) onCreate} yet。
INITIALIZED,
* Created state for a LifecycleOwner。 For an {@link android。app。Activity}, this state
* is reached in two cases:
*
- after {@link android。app。Activity#onCreate(android。os。Bundle) onCreate} call;
*
- right before {@link android。app。Activity#onStop() onStop} call。
*
*
CREATED,
* Started state for a LifecycleOwner。 For an {@link android。app。Activity}, this state
*
*
STARTED,
* Resumed state for a LifecycleOwner。 For an {@link android。app。Activity}, this state
* is reached after {@link android。app。Activity#onResume() onResume} is called。
RESUMED;
* Compares if this State is greater or equal to the given {@code state}。
*
* @param state State to compare with
* @return true if this State is greater or equal to the given {@code state}
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
**可以看到其中原始碼的解釋,結合`isAtLeast(@NonNull State state)`方法,你就會很明白,什麼是lifecycle中的活躍狀態,就是在狀態STARTED及之後的狀態,我們稱為活躍狀態。**
然後我們在往下分析在
`onStateChanged(LifecycleOwner source, Lifecycle。Event event)`
方法裡看到,當我們獲取到Lifecycle的狀態處於`DESTROYED`時就會移除資料觀察者。在`activeStateChanged(boolean newActive)`方法中,當處於活躍狀態,資料變化就會push到觀察者,從而實時更新UI。
原始碼大體上就是這樣,那我們接下來就關注下LiveData的優點,和怎麼使用吧。
###使用LiveData的優點
- **確保UI與資料狀態匹配**
LiveData遵循觀察者模式。當生命週期狀態改變時,你可以在觀察者中更新UI,而不是直接在生命週期方法中操作。
- **沒有記憶體洩漏**
觀察者繫結到生命週期物件,並在其關聯的生命週期被銷燬後自行清理。
- **activity停止後不會造成crash**
觀察者的生命週期是不活躍的,例如在後臺執行的情況下,它不接收任何LiveData事件。
- **不再人工生命週期處理**
UI元件只是觀察相關資料,不停止或恢復觀察。LiveData自動管理所有這一切,因為它會感知、觀察到相關的生命週期狀態變化。
- **始終保持最新資料**
因為採用觀察者模式,所以我們能實時的知道資料的變化。
如果生命週期變得不活躍,則在再次啟用時接收最新資料。例如,在後臺返回到前臺後立即接收最新資料。
- **正確的配置改變**
如果由於配置改變(如裝置旋轉)而重新建立activity或fragment,則它立即接收最新的可用資料。
- **資源共享**
你可以使用Sigelon模式擴充套件LiveData物件來包裝系統服務,以便它們可以在你的應用程式中共享。LiveData物件連線到系統服務一次,然後需要該資源的任何觀察者都可以只觀察LiveData物件。
###LiveData使用
LiveData遵循觀察者模式。當生命週期狀態改變時,LiveData通知觀察者物件。這樣很容易對程式碼進行維護,諸多好處這裡不在贅述。
- 1。建立一個LiveData例項來儲存某種型別的資料。這通常是在View模型類中完成的(這個類實際上就是我們說的ViewModel)。
- 2。建立一個Observer觀察者物件,該物件定義onChanged()方法,該方法會響應LiveData物件儲存的資料更改或變化。通常在UI控制器中建立一個觀察物件,例如activity或fragment。
- 3。使用`observe() `觀察者方法將觀察者物件附加到LiveData物件。`observe() `方法會持有一個`LifecycleOwner`物件。通常將觀察者物件附加在UI控制器中,例如activity或fragment。
當更新儲存在LiveData物件中的值時,只要附加的LifecycleOwner處於活動狀態,它就會觸發所有註冊的觀察者。LiveData允許UI控制器裡的觀察者訂閱更新。當LiveData物件儲存的資料發生變化時,UI會自動響應更新。
LiveData採用泛型,是一個可以與任何資料一起使用的包裝器,包括實現集合的物件,如list。LiveData物件通常儲存在ViewModel物件中,並透過getter方法訪問,如下面的示例所示:
public class NameViewModel extends ViewModel {
// Create a LiveData with a String
private MutableLiveData
public MutableLiveData
if (mCurrentName == null) {
mCurrentName = new MutableLiveData
return mCurrentName;
// Rest of the ViewModel。。。
***********************
確保將更新UI的LiveData物件儲存在ViewModel物件中,而不是在activity或fragment中,原因如下:
- **避免臃腫的fragment和activity。現在這些UI控制器負責顯示資料,但不儲存資料狀態。**
- **將LiveData例項與特定fragment和activity例項解耦,並允許LiveData物件在配置更改中生存。**
**另外,資料的觀察,最好是在`onCreate()`方法中進行,因為這樣可以避免多次呼叫,以確保LifecycleOwner在其到達活躍狀態後立即顯示資料**
************
下面我們看看資料是如何被觀察的,我們看程式碼。
public class NameActivity extends AppCompatActivity {
private NameViewModel mModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super。onCreate(savedInstanceState);
// Other code to setup the activity。。。
// Get the ViewModel。
mModel = ViewModelProviders。of(this)。get(NameViewModel。class);
// Create the observer which updates the UI。
final Observer
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView。
mNameTextView。setText(newName);
};
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer。
mModel。getCurrentName()。observe(this, nameObserver);
當我們呼叫`observe(@NonNull LifecycleOwner owner, @NonNull Observer
**那麼我們是怎麼釋出或者更新資料的呢?**
LiveData 沒有公開可用的方法來更新儲存的資料。所以我們會使用繼承了LiveData 的子類MutableLiveData類的setValue(T)和postValue(T)方法,如果需要編輯LiveData物件中儲存的值,則必須使用這些方法。
/**
* {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method。
*
* @param
*/
public class MutableLiveData
public void postValue(T value) {
super。postValue(value);
public void setValue(T value) {
super。setValue(value);
在建立了觀察者關係之後,然後可以更新LiveData物件的值,如下面的示例所示,當用戶點選按鈕時觸發所有觀察者:
mButton。setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String anotherName = “John Doe”;
mModel。getCurrentName()。setValue(anotherName);
});
**必須呼叫`setValue(T)`方法來從主執行緒更新LiveData物件。如果在工作執行緒中執行程式碼,則可以使用`postValue(T)`方法來更新LiveData物件。**
###Transform LiveData
在將LiveData物件分配給觀察者之前,你可能需要對儲存在LiveData物件中的值進行更改,或者您可能需要基於另一個LiveData物件的值返回不同的LiveData例項。生命週期包提供[Transformations](https://developer。android。google。cn/reference/android/arch/lifecycle/Transformations。html)
轉換類,其中包含支援這些轉換的方法。
- [Transformations。map()](https://developer。android。google。cn/reference/android/arch/lifecycle/Transformations。html#map(android。arch。lifecycle。LiveData%3CX%3E,%20android。arch。core。util。Function%3CX,%20Y%3E))使用這樣的方法,將需要的值傳遞到下游。
LiveData
LiveData
user。name + “ ” + user。lastName
透過轉換,我們把user轉化成了name和lastName傳遞下去。
是不是感覺這部分的概念有點熟悉?沒錯,這裡的`map()`和我們`RxJava`中的`map()`運算子很相似,和我們的java中的`Stream`裡的高階函式`map()`也很相似。其實他們的轉換理念或者說核心思想都一樣,都是把物件變成流來操作。
- [Transformations。switchMap()](https://developer。android。google。cn/reference/android/arch/lifecycle/Transformations。html#switchMap(android。arch。lifecycle。LiveData%3CX%3E,%20android。arch。core。util。Function%3CX,%20android。arch。lifecycle。LiveData%3CY%3E%3E))
private LiveData
。。。;
LiveData
LiveData
和上面的`map()`方法很像。區別在於傳遞給`switchMap()`的函式必須返回LiveData物件。
和LiveData一樣,Transformation也可以在觀察者的整個生命週期中存在。只有在觀察者處於觀察LiveData狀態時,Transformation才會運算。Transformation是延遲運算的(calculated lazily),而生命週期感知的能力確保不會因為延遲發生任何問題。
這部分程式碼看不明白?迷迷糊糊的?那我們還是使用終極手段——檢視原始碼中的`switchMap()`是怎麼說的。
* Creates a LiveData, let’s name it {@code swLiveData}, which follows next flow:
* it reacts on changes of {@code trigger} LiveData, applies the given function to new value of
* {@code trigger} LiveData and sets resulting LiveData as a “backing” LiveData
* to {@code swLiveData}。
* “Backing” LiveData means, that all events emitted by it will retransmitted
* by {@code swLiveData}。
*
* If the given function returns null, then {@code swLiveData} is not “backed” by any other
* LiveData。
*
* The given function {@code func} will be executed on the main thread。
* Consider the case where you have a LiveData containing a user id。 Every time there‘s a new
* user id emitted, you want to trigger a request to get the user object corresponding to that
* id, from a repository that also returns a LiveData。
* The {@code userIdLiveData} is the trigger and the LiveData returned by the {@code
* repository。getUserById} is the “backing” LiveData。
* In a scenario where the repository contains User(1, “Jane”) and User(2, “John”), when the
* userIdLiveData value is set to “1”, the {@code switchMap} will call {@code getUser(1)},
* that will return a LiveData containing the value User(1, “Jane”)。 So now, the userLiveData
* will emit User(1, “Jane”)。 When the user in the repository gets updated to User(1, “Sarah”),
* the {@code userLiveData} gets automatically notified and will emit User(1, “Sarah”)。
* When the {@code setUserId} method is called with userId = “2”, the value of the {@code
* userIdLiveData} changes and automatically triggers a request for getting the user with id
* “2” from the repository。 So, the {@code userLiveData} emits User(2, “John”)。 The LiveData
* returned by {@code repository。getUserById(1)} is removed as a source。
*
* MutableLiveData
userIdLiveData = 。。。; * LiveData
userLiveData = Transformations。switchMap(userIdLiveData, id -> * repository。getUserById(id));
* void setUserId(String userId) {
* this。userIdLiveData。setValue(userId);
* }
*
* @param trigger a {@code LiveData} to listen to
* @param func a function which creates “backing” LiveData
* @param
* @param
@MainThread
public static
@NonNull final Function
final MediatorLiveData
result。addSource(trigger, new Observer
LiveData
public void onChanged(@Nullable X x) {
LiveData
if (mSource == newLiveData) {
return;
}
if (mSource != null) {
result。removeSource(mSource);
mSource = newLiveData;
result。addSource(mSource, new Observer
@Override
public void onChanged(@Nullable Y y) {
result。setValue(y);
}
});
});
return result;
嗯。。。好像有點長哈,太長不看。那我就來幫大家翻譯翻譯。
假如我們有一個User實體類,而在我們的資料倉庫中(我們暫時不關心倉庫的概念),有這樣兩個資料,`User(1, “Jane”)` 和 `User(2, “John”)`。
我們也有了userIdLiveData,其中我們能拿到需要的使用者id。每次發出新的使用者id,您都希望觸發一個請求,從還返回LiveData的儲存庫中獲取與該id對應的使用者物件。(也就是這樣一種場景,我們要從api中獲取我需要的使用者id,然後根據id獲取到我需要的使用者)
在儲存庫(假設在網路)包含User(1,“Jane”)和User(2,“John”)的情況下,當userIdLiveData值被設定為“1”時,`switchMap`將呼叫`getUser(1)`,它將返回包含值`User(1,“Jane”)`的LiveData,userLiveData將發出使用者(1,“jane”)。當儲存庫中的使用者更新到User(1,“Sarah”)時,userLiveData將自動得到通知,並將發出User(1“Sarah”)。
當使用userId=“2”呼叫setUserId方法時,userIdLiveData的值發生變化,並自動觸發從儲存庫中獲取id=“2”的使用者的請求。因此,userIdLiveData發出使用者(2,“john”)。
下面貼上例子:
首先建立我們的ViewModel
public class StudyViewModel extends ViewModel {
private InitDataRepository initDataRepository = new InitDataRepository();
private LiveData
private MutableLiveData
//獲取user的方法
public LiveData
userLiveData=Transformations。switchMap(userId,
new Function
@Override
public MutableLiveData
return initDataRepository。getUserById(input);
}
});
return userLiveData;
//這裡模擬我們動態的設定需要的 user id
public void setUserId(int require) {
userId = initDataRepository。getUserId(require);
**我們把資料的獲取方式獨立出來,這就是前面多次提到的`Repository`概念。這個概念的意思就是把獲取資料的這部分功能透過一個新的Repository類代理執行。這個Repository類的作用就是獲取並提供各種來源的資料(資料庫中的資料,網路資料,快取資料等),並且在來源資料更新時通知資料的獲取方ViewModel,這樣就大大減少了ViewModel的程式碼量。**
public class InitDataRepository {
private MutableLiveData
private MutableLiveData
public MutableLiveData
//我們這裡假設透過網路請求我們拿到需要的User
User user=new User(id,“user_name”+id);
users。setValue(user);
return users;
public MutableLiveData
//我們這裡假設透過網路請求,我們拿到id=require;
userId。setValue(require);
return userId;
我們在activity中觀察我們的資料,當我們點選一次就更新一次UI
public class MainActivity extends AppCompatActivity {
private StudyViewModel studyViewModel;
private TextView text;
private int count=0;
setContentView(R。layout。activity_main);
text=findViewById(R。id。text);
studyViewModel = ViewModelProviders。of(this)
。get(StudyViewModel。class);
* 觀察資料的變化,及時更新
//注意,在本例子中必須要先呼叫這個方法,不然getUserLiveData()會空指標
studyViewModel。setUserId(count);
studyViewModel。getUserLiveData()。observe(this, new Observer
public void onChanged(@Nullable User user) {
text。setText(user。toString());
public void click(View view) {
count++;
## 合併多個LiveData中的資料
[MediatorLiveData](https://developer。android。google。cn/reference/android/arch/lifecycle/MediatorLiveData。html)
是LiveData的子類,可以透過`MediatorLiveData`合併多個LiveData來源的資料。同樣任意一個來源的LiveData資料發生變化,`MediatorLiveData`都會通知觀察他的物件。說的有點抽象,舉個例子。比如UI接收來自本地資料庫和網路資料,並更新相應的UI。可以把下面兩個LiveData加入到MeidatorLiveData中:
* 關聯資料庫的LiveData
* 關聯聯網請求的LiveData
相應的UI只需要關注MediatorLiveData就可以在任意資料來源更新時收到通知。