基於 Android Architecture Components 的 MVVM 淺析

逗比的狼發表於2019-04-20

0、前言

官方文件永遠是最好的學習資料:
Android Jectpack
Android Jetpack: LiveData 和 Lifecycle 介紹 | 中文教學視訊
Android Jetpack - ViewModel | 中文教學視訊
Android Jetpack Room | 中文教學視訊
深入瞭解還需多看文件和原始碼。

1、簡介

1.1、AAC 是什麼

AAC (Android Architecture Components) 是谷歌推出的一套包含在 Jetpack 中的,幫助我們構建穩健、可測試且易維護應用的元件庫,主要包括 LifecycleLiveDataViewModelRoomWorkManager 等一系列好用的工具。注意,AAC 並不是一種新的架構,只是一套和架構相關的工具,可以幫助你更加簡單高效的構建你想要的架構。

1.2、AAC 與 MVVM

MVC (Model-View-Controller)、MVP (Model-View-Presenter) 和 MVVM (Model-View-ViewModel) 在 Android 中的應用大概可以概括為下圖(架構分層因人而異,這裡只是我自己的一些理解)

基於 Android Architecture Components 的 MVVM 淺析

在 MVP 的架構中,View 層和 Presenter 層相互引用對方,Presenter 層收到 View 層的動作或者拿到 Model 層的資料後主動呼叫 View 的一些方法,顯示相應的結果。Presenter 層就像個全職保姆一樣,上有 View 層要處理動作和顯示,下有 Model 層要請求和處理資料,中間自己還要處理業務邏輯。我們一般需要定義 IViewIPresenter 之類的介面,然後 View 層和 Presenter 層相互持有對方的引用,這樣就存在很多問題,比如:

  • View 和 Presenter 解耦不徹底,Presenter 需要具體知道 View 層的能力
  • 因為 View 層和 Model 層之間的通訊和其他業務層面的大小事務都由它來處理,會導致 Presenter 過度膨脹
  • View 層的生命週期和 Presenter 層不一致,以及記憶體洩漏等問題
    注意 弱引用只能解決記憶體洩露的問題,無法解決生命週期的問題,比如 Activity 已經 onDestroy,但並沒有被回收的場景
  • 擴充套件性差,增刪 View 層或變更業務要改動很多程式碼

MVVM 採用 View 層與 ViewModel 層的資料繫結的方式,View 層監聽相應的資料,並在資料變更時自己更改檢視,從而很好地解決了上述問題:

  • View 層和 ViewModel 層鬆耦合,ViewModel 層不需要持有具體的 View,也不需要知道 View 層的任何東西
  • ViewModel 層很輕,ViewModel 層只需要把新的資料通知到各個 View,而不關心 View 的顯示
  • 由於 ViewModel 層不直接引用 View 層,生命週期更好處理
  • ViewModel 層不關心 View 層的變更,不也不關心 View 的數量,甚至不關心監聽的是不是 View

可見 MVVM 更為先進好用,實現 MVVM 的方法也有很多,而 AAC 就是為 MVVM 而生的,通過 AAC 中的 LiveDataViewModel 等元件,我們可以很容易地在 Android 上實現 MVVM。它的 Lifecycle 元件可以讓我們更有效的管理 app 內的各種生命週期,在配置變更時儲存資料,避免記憶體洩漏,更方便地把資料載入到 UI 中;LiveData 用來構建一個可以在資料變更時通知檢視的資料物件,且具有生命週期感知的能力;ViewModel 可以儲存 UI 相關的資料,並保證在配置變更時不會丟失。

2、基於 AAC 的 MVVM 簡單用法

2.1、LiveData

LiveData 是一個可觀察的資料持有類,而且它可以感知其他應用元件 (如 ActivityFragmentService) 的生命週期,這種感知可確保 LiveData 僅更新生命週期處於啟用狀態(STARTED 和 RESUMED)的觀察者。它的主要優點有:

  • UI 與資料同步,利用觀察者模式,可以在資料變化時通知 UI
  • 不存在記憶體洩露,會在觀察者對應的生命週期結束後自動移除觀察者
  • 不會更新非啟用狀態(STOPPED)狀態的 UI,以免造成崩潰(Fragment Transaction)
  • 不需要手動處理生命週期事件
  • UI 從非啟用狀態切換到啟用狀態時,會收到 LiveData 的最新資料,資料預載入不再需要考慮 View 的狀態
  • ActivityFragment 重建時也會收到通知(需要和 ViewModel 配合使用)

簡單用法。假設我們的 MainActivity 的檢視中有一個 Button 和一個 TextView 來實現一個簡單的計數器,還有一個計數值的 LiveData。點選 Button 會更新計數值,而 TextView 監聽該 LiveData 來更新自己。示例分析如下:

public class MainActivity extends AppCompatActivity {
    // 1、MutableLiveData 是什麼
    private MutableLiveData<Integer> mLiveData = new MutableLiveData<>();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化工作
        // ...
        
        mLiveData.setValue(0);
        
        // 2、LiveData.observe() 的引數都是什麼意思
        // public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)
        mLiveData.observe(this, new Observer<Integer>() {
            @Override
            public void onChanged(@Nullable final Integer count) {
                // Update the UI, in this case, a TextView.
                mTextView.setText(newName);
            }
        });
        
        // 3、LiveData 的值怎麼更新
        mButton.setOnClickListener(v -> mLiveData.setValue(mLiveData.getValue() + 1));
    }
}
複製程式碼

1、首先 LiveData 是一個抽象類,且 setValuepostValue 兩個更新 value 的方法都是 protected 的,而 MutableLiveData 繼承 LiveData,重寫這兩個方法,並將訪問許可權設定為 public

2、public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)
第一個引數 LifecycleOwner 是持有生命週期的物件,比如 ActivityFragment,可以讓 LiveData 可以感知它的生命週期,並在生命週期結束時將其移除,避免記憶體洩漏;第二個引數 Observer 是一個介面,它只有一個方法 void onChanged(T t)LiveData 會在資料更新時呼叫這個函式來通知 UI 層的觀察者。

3、setValuepostValue 兩個都可以更新 value,不同之處在於 setValue 只能在主執行緒呼叫,而 postValue 可以用於子執行緒(注意:短時間內多次 postValueLiveData 只會保留最後一個來通知觀察者)。

當然,這只是LiveData 用法的簡單說明,實際專案中如果這樣用會有些問題:

  • 把資料相關的東西放在了 View 層,當然我們可以單獨抽一層放 LiveData 來解決這個問題
  • 在配置變更(如螢幕旋轉)時,ActivityFragment 會重建導致資料丟失,這個問題當然可以通過 onSaveInstanceState 來保留和恢復資料;
  • 如果多個 View 都需要同樣的資料來源或者相互通訊,難以保證拿到同一個 LiveData,單例可以解決這個問題,但資料來源不在被需要時也無法回收資源。

上面說的這些問題都可以通過某些手段解決,但是都不是很優雅,而谷歌當然考慮到了,這些問題在接下來的 ViewModel 中,都得到了很好的解決。

2.2、ViewModel

ViewModel,顧名思義,是用來儲存和管理 View 相關資料的,而 AAC 中的 ViewModel 還可以感知生命週期,可以在配置變更(如螢幕旋轉)時自動儲存資料,還可以在生命週期真的結束時觸發回撥來清除不必要的請求,以免記憶體洩漏。而作為 MVVM 的中間層,它還肩負著響應 View 層的動作,以及操作 Model 層請求資料的任務。ViewModel 的生命週期如下圖所示:

基於 Android Architecture Components 的 MVVM 淺析

注意ViewModel 由於生命週期是長於 View 層(ActivityFragmentView)的,不能(也不需要)持有 View 層的任何東西,如果要使用 context 可以繼承 AndroidViewModel,它內部持有 Application 的 context。
簡單用法。

public class MyViewModel extends ViewModel {
    // ...
    
    public List<User> getUsers() {
        return users;
    }

    public void loadUsers() {
        // 請求 users 資料.
    }
    
    // 1、呼叫時機
    @Override
    protected void onCleared() {
        // 清除不必要的請求 
    }
}

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // MyActivity 重建時還是能拿到同一個 MyViewModel
        // 2、ViewModelProviders.of 引數
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.loadUsers();
    }
}
複製程式碼

1、ViewModelonCleared 函式會在持有它的物件的生命週期結束時呼叫,以免非同步請求造成 ViewModel 的記憶體洩露;

2、public static ViewModelProvider of(@NonNull FragmentActivity activity)public static ViewModelProvider of(@NonNull Fragment fragment),傳入的引數可以是 ActivityFragment,其內部會拿到 ActivityFragmentViewModelStore,顧名思義就是儲存 ViewModel 的地方,其內部也只是一個 HashMap<String, ViewModel>,鍵是內部用 ViewModelClass 的名字拼出的字串。

利用 ViewModelStore 儲存 ViewModel 可以十分方便地管理 ViewModel ,前面說的 ViewModel 可以在配置變更後存活,其實就是在重建儲存狀態時,儲存下了 ViewModelStore,實現方式和儲存 Fragment 類似(Bundle 儲存的資料是有限的,為了在配置變更時儲存大量資料,也可以用 Fragment 來存)。利用 ViewModelStore 儲存 ViewModel 的方式還可以方便 Fragment 之間的通訊和資料同步,只要多個 Fragment 隸屬於 同一個 Activity,他們就可以通過 ActivityViewModelStore 拿到同一個 ViewModel

2.3、MVVM

雖然 LiveDataViewModel 單獨拿出來用也是強有力的工具,谷歌推出 AAC 的目的明顯不僅僅是一個工具,這一整套服務於的架構相關的元件可以幫助我們輕鬆的打造 MVVM,而且都帶著生命週期感知能力。接下來通過一個計數器例子,簡單介紹下使用方法。

public class CountViewModel extends ViewModel {
    private final MutableLiveData<Integer> mCountLiveData = new MutableLiveData<Integer>();
    
    public LiveData<Integer> getCountLiveData() { return mCountLiveData; }

    public void loadCount() {
        // 可以通過網路或資料庫請求資料
        request.enquen(response -> { 
            if (response.isSuccess()) {
                mCountLiveData.postValue(response.data);
            }
        });
    }
    
    public void countDown() {
        // 減小計數
        if (mCountLiveData.getValue() != null) {
            mCountLiveData.setValue(mCountLiveData.getValue() - 1);
        } else {
            loadCount();
        }
    }
    
    public void countUp() {
        // 增大計數
        if (mCountLiveData.getValue() != null) {
            mCountLiveData.setValue(mCountLiveData.getValue() + 1);
        } else {
            loadCount();
        }
    }
}

public class ActionFragment extends Fragment {
    //...
    
    @Override
    public void onViewCreated(View v, Bundle savedInstanceState) {
        final CountViewModel countViewModel =  ViewModelProviders.of(getActivity()).get(MyViewModel.class);
        // 改變計數值
        v.findViewById(R.id.up_buttonn).setOnClickListener(v -> {
            countViewModel.countUp();
        });
        v.findViewById(R.id.down_buttonn).setOnClickListener(v -> {
            countViewModel.countDown();
        });
    }
    
    //...
}

public class ShowCountFragment extends Fragment {
    //...
    
    @Override
    public void onViewCreated(View v, Bundle savedInstanceState) {
        final TextView count = v.findViewById(R.id.count_text_view);
        // 這裡的 getActivity() 是為了拿到同一個 CountViewModel
        ViewModelProviders.of(getActivity())
            .get(CountViewModel.class)
            .getCountLiveData()
            // 這裡的 this 是為了讓 LiveData 繫結觀察者的生命週期
            .observe(this, data -> {
                count.setText(data);
            });
    }
    
    //...
}
複製程式碼

在該例子中,CountViewModel 中有一個 mCountLiveData 用於儲存計數值,還有一組用於更新計數值的方法;ActionFragmentShowCountFragment 位於同一個 Activity 中,這樣可以保證兩者拿到同一個 CountViewModelActionFragment 的兩個按鈕用於增減計數,而 ShowCountFragment 則監聽並顯示計數值。

這樣一個簡單的 MVVM 架構的計數器就搭建好了。View 層的 ShowCountFragment 繫結 ViewModel 層的 mCountLiveData,並在資料變更時更新檢視,而 ViewModel 層不需要直接持有任何 View 層的引用(LiveData 持有的觀察者在 View 層,但是會自動根據生命週期來移除),ViewModel 也不關心監聽資料的 View 的數量和型別,View 拿到資料後顯示什麼東西也都無所謂,多一個 View 只不過是多了一個觀察者而已,而且多個 View 不需要藉助其他工具(EventBus、RxBus 等事件匯流排)就可以通過 ViewModel 實現通訊。

對於資料來源比較多的場景,谷歌建議我們單獨抽出 Repository 層(其實就是 Model 層)用於處理資料來源(快取、資料庫或網路),並向上返回資料的 LiveData(如 Room)來保持資料的同步,整個架構圖如下圖所示:

MVVM

3、進一步瞭解 AAC

**配合原始碼使用,效果更佳!**特別推薦使用 androidx 來看原始碼,會清晰方便很多。

3.1、Lifecycle

View 層的動態性很強,各個介面切換、檢視元素交替出現等都伴隨著生命週期的變化,而下層元素的生命週期往往要長於 View 的生命週期,為了不造成資源浪費和記憶體洩漏,我們時常需要手動管理 View 的生命週期。比如我們有一個顯示當前位置的 Activity,我們需要在 onStart 時開始監聽位置資訊,並在位置變化時更改檢視,在 onStop 時登出監聽。

基於 Android Architecture Components 的 MVVM 淺析

手動管理 ActivityFragment 的生命週期是一件十分繁瑣而且低效的事情,為了更有效地管理生命週期,許多第三方庫(例如 Glide、RxLifecycle)都將監聽與分發生命週期地任務交給 Frgament,因為只要將 Frgament 塞進 Activity 中,Frgament 就能與 Activity 生命週期同步,然後通過自定義的 Frgament 將生命週期事件傳送出來。AAC 中的 Lifecycle 元件也是通過這種方式和觀察者模式實現了生命週期地自動管理。

Lifecycle 元件主要有 LifecycleLifecycleObserver 和一套相關地類組成。Lifecycle 定義了一系列生命週期地狀態和事件,其唯一子類 LifecycleRegistry 則實現了一個作為生命週期被觀察者所具有的能力,在生命週期變化時向觀察者們分發事件。LifecycleObserver 是一個起標識作用地介面,它的子介面 LifecycleEventObserveronStateChanged 方法在 LifecycleRegistry 分發生命週期時會被回撥。

現在問題來了:

  • 我們怎麼拿到 Lifecycle

Lifecycle 的持有者都會實現 LifecycleOwner 介面,重寫 Lifecycle getLifecycle();方法,返回自己持有的生命週期物件 LifecycleRegistryActivityFragment 都實現了該介面。

  • Lifecycle 的生命週期事件從哪裡來,和之前說的 Frgament 有什麼關係?

LifecycleOwner 的實現類既然持有 Lifecycle,那肯定就會傳送事件啦。Frgament 的話比較簡單,內部儲存了一個 mLifecycleRegistry,並在自己的生命週期事件中呼叫 mLifecycleRegistryhandleLifecycleEvent 方法將事件傳遞給 LifecycleRegistryActivity 自己也持有一個 mLifecycleRegistry,但是它不自己傳送事件,而是把任務交給了一個叫 ReportFragmentFragment,其實現也很簡單,就是在自己的生命週期事件中拿到 ActivitymLifecycleRegistry,然後再進行分發。

  • LifecycleObserver 怎麼用

需要監聽 Lifecycle 的物件實現這個介面,然後在 onStateChanged 方法中根據不同的生命週期做出相應的動作。比如前面說的監聽位置資訊的例子,我們就可以單獨抽出一個物件來專門做監聽操作,實現 LifecycleObserver 介面,並自己處理生命週期事件。

class MyLocationListener implements LifecycleObserver {
    private boolean enabled = false;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       //...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void start() {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void stop() {
        // disconnect if connected
    }
}
複製程式碼

3.2、ViewModel

這裡我們來扒一扒原始碼,看 ViewModel 是如何建立、如何挺過配置變更,又是何時真正的消失的。

  • ViewModel 的建立

前面我們已經知道 ViewModel 都儲存在 ViewModelStore 中,那隻要知道 ViewModelStore 如何被建立、儲存與銷燬就行。在 ComponentActivitygetViewModelStore 方法可以看到 ViewModelStore 的建立過程。

if (mViewModelStore == null) {
    NonConfigurationInstances nc =
            (NonConfigurationInstances) getLastNonConfigurationInstance();
    if (nc != null) {
        // Restore the ViewModelStore from NonConfigurationInstances
        mViewModelStore = nc.viewModelStore;
    }
    if (mViewModelStore == null) {
        mViewModelStore = new ViewModelStore();
    }
}
複製程式碼

可以看到 ViewModelStore 是從一個叫 NonConfigurationInstances 的例項中拿的,如果拿不到說明之前沒經歷過配置變更,那就 new 一個出來。接著看

// ComponentActivity 裡的
static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}
複製程式碼

NonConfigurationInstances 除了儲存 ViewModelStore 還存著 custom 用於儲存我們自己定製的資料,這個可以通過重寫 onRetainCustomNonConfigurationInstance 方法來用。再來看下 getLastNonConfigurationInstance

public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}
// 這個是 Activity 中的,和 ComponentActivity 裡的那個不一樣
static final class NonConfigurationInstances {
    Object activity;
    HashMap<String, Object> children;
    FragmentManagerNonConfig fragments;
    ArrayMap<String, LoaderManager> loaders;
    VoiceInteractor voiceInteractor;
}
複製程式碼

是在 ActivityNonConfigurationInstances 中的 activity,通過 NonConfigurationInstances 我們也能大致看出 Fragment 在配置變更的時候會被儲存到 FragmentManagerNonConfig 中。

  • 接下來看如何挺過配置變更的

ComponentActivityonRetainNonConfigurationInstance 中會分別拿我們定製的 customViewModelStore,然後返回建立好的 NonConfigurationInstances。而 onRetainNonConfigurationInstance 會在配置變更時被 LocalActivityManagerdispatchRetainNonConfigurationInstance 方法中呼叫,從而儲存狀態資訊。至此我們就知道儲存 ViewModelStore 的流程,再繼續深入原始碼就無法自拔了。

  • 最後看下什麼時候真正的銷燬 ViewModel,呼叫它的 onCleared
getLifecycle().addObserver(new GenericLifecycleObserver() {
    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            if (!isChangingConfigurations()) {
                getViewModelStore().clear();
            }
        }
    }
});
複製程式碼

ComponentActivity 的建構函式中有這麼一段程式碼,剛好用到了我們前面說的 LifecycleLifecycleObserver,在 ON_DESTROY 時判斷下是否是配置變更,不是的話就呼叫 ViewModelStoreclear 方法,會清除 ViewModelStore 中儲存的 ViewModel,並呼叫他們的 clear 方法,進而呼叫到 onCleared

至此,Activiy 中的 ViewModel 相關生命週期已經分析完了,Fragment 中也大同小異,主要涉及 FragmentManagerImplFragmentManagerViewModel 等一些類,感興趣的可以順著 ViewModelStore 的思路,自己深入瞭解下。

3.3、LiveData 擴充套件用法

觀察者模式的那套東西都可以玩一些騷操作,責任鏈、事件匯流排什麼的,LiveData 作為一個可觀察物件,當然也可以,這裡簡單分析兩個。

首先了解下一個叫 MediatorLiveData 的物件,它繼承自 MutableLiveData,通過 public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) 方法實現了增加其他 LiveData 作為自己事件源的功能,源 LiveData 更新時,會呼叫傳入的 ObserveronChanged 方法做處理。MediatorLiveData 是利用 public void observeForever(@NonNull Observer<? super T> observer) 方法來新增源的,這個方法不需要傳 LifecycleOwner,但是需要手動移除觀察者,不過不用擔心,MediatorLiveData 已經幫我們做了。如果 MediatorLiveData 已經沒有任何觀察者,它會自動呼叫源 LiveDataremoveObserver 方法來移除對源 LiveData 的監聽,以防自己記憶體洩漏。

利用 MediatorLiveData 物件,我們可以做一些事件變換的操作,TransformationsmapswitchMap 就是通過該物件實現的。

之前的事件匯流排都需要手動處理生命週期的問題,EventBus 需要手動登出,RxBus 需要 RxLifecycle 的擴充套件庫來監聽生命週期。有了 LiveData,我們完全可以用很少的程式碼擼一個具有生命週期感知能力的事件匯流排,實現很簡單(一個簡單但實用的 LiveDataBus 只需要一百行程式碼左右),網上也有很多開源的庫,這裡只講下大致思路。

要想做事件匯流排,核心就是傳送方和接收方拿到同一個可觀察物件,在這裡就是同一個 LiveData。最簡單的方法就是定義一個單例類,假設叫 LiveDataBus,裡面放一個 Map<Class<?>, MutableLiveData<?>> 來儲存所有的事件型別 Class 和對應的 LiveData,這樣就能通過事件的 Class 拿到對應的唯一 LiveData。這裡有幾個需要注意的地方:

  • Map 的併發問題,因為來拿 LiveData 的可能多個執行緒的,所以要做好同步工作,再考慮到可見性,建議用 ConcurrentHashMap
  • Map 裡存的 ClassMutableLiveData 都被擦出了泛型(不同事件的 Class<T> 裡泛型肯定不一樣呀),所以要自己保證兩者的泛型一致,可以定義一個儲存 Class<T>MutableLiveData<T> 的帶泛型 <T> 的類,在裡面保證一致;
  • LiveDataBus 中的 LiveData 不會被釋放,也就導致它裡面的資料不會被釋放,可以在事件不用時傳送一個空事件;
  • 全域性的事件匯流排會造成業務間的耦合,而且不好除錯,除非關鍵事件,不要濫用。

注意ViewModel 已經能夠解決很多場合的通訊問題,而且能在不用時釋放掉,所以能用 ViewModel 通訊的就不要用事件匯流排,除非兩個 Activity 必須通過某種方式進行通訊,也要先考慮下單例的 LiveData,實在不行再用事件匯流排。

最後再強調一遍,不要濫用事件匯流排

3.4、LiveData 原始碼分析

LiveData 原始碼不多,這裡只簡單分析幾個 LiveData 的特性。

  • 如何在觀察者生命週期結束自動移除觀察者

LiveData 的訂閱方法看起

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    // ...
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // ...
    owner.getLifecycle().addObserver(wrapper);
}
複製程式碼

可以看到 LiveData 不僅是一個可觀察物件,同時還是一位觀察者,它所觀察的就是 ActivityFragment 等持有 LifecycleLifecycleOwner,當然也要把觀察自己的觀察者儲存下來,接下來看下 LifecycleBoundObserver 這個觀察者是怎麼處理 Lifecycle 的。首先它繼承自 ObserverWrapper 這是一個會根據 Lifecycle 是否處於啟用狀態決定是否分發資料的觀察者,這裡先跳過,LifecycleBoundObserver 還實現了 GenericLifecycleObserver 介面,其實就是前面說過的 LifecycleEventObserver,它的生命週期狀態回撥函式如下:

// LifecycleBoundObserver 的方法
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
    if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
        removeObserver(mObserver);
        return;
    }
    activeStateChanged(shouldBeActive());
}

// LiveData 的方法
public void removeObserver(@NonNull final Observer<? super T> observer) {
    // ...
    ObserverWrapper removed = mObservers.remove(observer);
    // ...
    removed.detachObserver();
    removed.activeStateChanged(false);
}

// LifecycleBoundObserver 的方法
void detachObserver() {
    mOwner.getLifecycle().removeObserver(this);
}
複製程式碼

onStateChanged 在判斷 Lifecycle DESTROYED 的時候呼叫 LiveDataremoveObserver,首先將觀察 LiveData 的觀察者移除,防止記憶體洩漏,之後再呼叫LifecycleBoundObserverdetachObserver 將自己從 LifecycleOwner 的觀察者中移除,自此將相互之間的觀察狀態接觸。

  • 觀察者的生命週期從非啟用態到啟用態時是怎麼收到通知的

做預載入的時候,我們可以在 ActivityFragment 建立時直接請求資料,塞到 LiveData,然後只要生命週期處於啟用狀態,不管什麼時候監聽 LiveData,都能收到最新的訊息。我們再來看一下 LifecycleBoundObserveronStateChanged 方法,它在最後呼叫了父類的 activeStateChanged(shouldBeActive()) 方法,來大致看一下

void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    // immediately set active state, so we'd never dispatch anything to inactive
    // owner
    mActive = newActive;
    boolean wasInactive = LiveData.this.mActiveCount == 0;
    LiveData.this.mActiveCount += mActive ? 1 : -1;
    if (wasInactive && mActive) {
        // 該方法是 LiveData 的
        onActive();
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) {
        // 該方法是 LiveData 的
        onInactive();
    }
    if (mActive) {
        dispatchingValue(this);
    }
}
複製程式碼

它首先會根據當前的啟用狀態進行去重,然後會根據 LiveData 的觀察者處於啟用狀態的數量和新的狀態判斷是否呼叫 onActiveonInactive,這兩個也是 LiveData 的重要回撥,不過都好理解,就不在細說了。最後會判斷現在是否是啟用狀態,是的話就呼叫 LiveDatadispatchingValue 方法,顧名思義就是 LiveData 向其觀察者傳送通知。而 dispatchingValue 接收一個 ObserverWrapper 的引數,如果不為空就是說只用通知這個特定的觀察者,否則通知所有處於啟用的觀察者。

  • setValuepostValue

setValue 直接改變當前的值,然後呼叫 dispatchingValue(null) 來通知所有啟用狀態的觀察者,不過必須在主執行緒呼叫否則會拋異常。因為其他執行緒也都可以直接改變當前值的話會造成併發,加鎖的話又會影響效能。

所以就又搞了一個 postValue,它首先在拿到同步鎖的情況下把值存到 mPendingData,然後向主執行緒的 Handler 拋一個更新當前值的 mPostValueRunnable,這個 mPostValueRunnable 在執行時也是先拿同步鎖,然後呼叫 setValue(現在在主執行緒)把 mPendingData 設定到當前值。在 mPostValueRunnable 丟擲去之後且還未執行前,如果再次呼叫 postValue 就又會修改 mPendingData 的值,而不會再次向 Handler 拋一次 mPostValueRunnable,這樣就導致了後設定的值覆蓋掉前面設定的,最後只會向觀察者們通知最新的值。這個是需要注意的點,谷歌可能認為既然只是在主執行緒更新 View,那你拿最新的值就行,其他的都無所謂,當然這樣也起到流量控制的作用,防止短時間內過多的事件觸發無用的回撥。

4、總結

谷歌推出的 AAC 庫很好的解決了日常使用中的生命週期問題,使我們可以專心於業務層面的設計,而不需要再為生命週期等問題擔憂。LiveDataViewModel 確實好用,但用的時候也有需要注意的地方,ViewModel 層實際應用中要上承 View 層,提供必要的動作和資料,下接 Model 層,做好資料處理,都有很多需要考慮的地方,之後有時間再談談我在使用 AAC 的路上的經驗和總結。
部落格的 Github 倉庫 歡迎大家 Start & Fork !

相關文章