Architecture Components 知識梳理(1) - Lifecycle 示例&原理

一、概述

聽說這一套ToolKit已經有很長一段時間了,一直沒有系統的學習過,前幾天有同事在項目中通過Lifecycles解決了監聽Activity生命周期的問題,不得不說,真的是一套強大的框架,除此之外,前段時間Google還更新了NavigationPagingWorkManager用來解決 頁面的管理 以及 任務的調度 問題,更激發了我學習的動力。

依照慣例,先從最基本的Demo學起,然后再去分析源碼。

二、導入依賴

根目錄build.gradle文件中,加上google()的遠程倉庫聲明(現在AS創建的工程已經默認包含了該聲明)。

allprojects {
    repositories {
        jcenter()
        //加上這一句。
        google()
    }
}

當我們使用了'com.android.support:appcompat-v7:27.1.1'時,其實已經默認導入了lifecycles相關的庫,由此可見,這確實是之后的一個趨勢。

默認導入的依賴

如果我們沒有使用support:appcompat,那么就需要自己去聲明依賴,當然并不是說要導入全部的依賴,而是可以根據需要去選擇對應的依賴庫,具體的說明在注釋中。

  • 選擇僅導入LiveDataViewModelLifecycles的其中一種,或者全部導入。
  • 注解處理器,在Java8上使用common-java8,在Java8以下使用compiler
  • 流式處理支持庫reactivestreams
  • 測試支持庫core-testing
dependencies {
    def lifecycle_version = "1.1.1"

    // ViewModel and LiveData
    implementation "android.arch.lifecycle:extensions:$lifecycle_version"
    // alternatively - just ViewModel
    implementation "android.arch.lifecycle:viewmodel:$lifecycle_version" // use -ktx for Kotlin
    // alternatively - just LiveData
    implementation "android.arch.lifecycle:livedata:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData), Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"

    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
    // alternately - if using Java8, use the following instead of compiler
    implementation "android.arch.lifecycle:common-java8:$lifecycle_version"

    // optional - ReactiveStreams support for LiveData
    implementation "android.arch.lifecycle:reactivestreams:$lifecycle_version"

    // optional - Test helpers for LiveData
    testImplementation "android.arch.core:core-testing:$lifecycle_version"
}

這里,我們直接使用appcompat當中導入的組件即可。

三、Lifecycle

3.1 解決的問題

Android開發當中,生命周期 是一個很重要的東西,因為我們往往需要在頁面創建onCreate、頁面展示onResume,和頁面銷毀onDestroy的時候,去進行業務邏輯的處理,而在此之前,我們只能通過ActivityFragment才能收到生命周期的回調,而其它的組件,例如Fragment當中的View需要收到生命周期的回調,那么只能通過ActivityFragment去通知它,這就造成了很多冗余的代碼,特別是當我們期望提供一個View作為SDK給接入方時,又無形中增加了接入方的成本。

如果任何一個對象,通過觀察者的方式,能夠收到Activity/Fragment生命周期的各種回調就好了。沒錯!Lifecycle就是這么一個東西!

3.2 示例

  • 首先,我們定義一個類LiveObserver,該類 實現LifecycleObserver接口,它定義了一系列的方法并用@OnLifecycleEvent(Lifecycle.Event.xxx)進行修飾,Lifecycle.Event的可選類型有如下幾種,很明顯可以看出是生命周期相關的東西。
    public enum Event {
        ON_CREATE,
        ON_START,
        ON_RESUME,
        ON_PAUSE,
        ON_STOP,
        ON_DESTROY,
        ON_ANY
    }

在這些方法中添加Log用于調試:

public class LiveObserver implements LifecycleObserver {

    private static final String TAG = LiveObserver.class.getSimpleName();

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void onCreate() {
        Log.d(TAG, "onCreate()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onStart() {
        Log.d(TAG, "onStart()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    void onResume() {
        Log.d(TAG, "onResume()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    void onPause() {
        Log.d(TAG, "onPause()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onStop() {
        Log.d(TAG, "onStop()");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy() {
        Log.d(TAG, "onDestroy()");
    }
}
  • 第二步,編寫測試的Demo,注意,這里我們是通過AppCompatActivity提供的getLifecycle#addObserver方法來實現對第一步中的LiveObserver實例進行監聽的:
public class LiveAppCompactActivity extends AppCompatActivity {

    private Button mBtnBind;
    private Button mBtnUnBind;
    private LifecycleObserver mObserver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live_appcompact);
        mBtnBind = findViewById(R.id.bt_bind);
        mBtnBind.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mObserver == null) {
                    mObserver = new LiveObserver();
                    getLifecycle().addObserver(mObserver);
                }
            }
        });
        mBtnUnBind = findViewById(R.id.bt_unbind);
        mBtnUnBind.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mObserver != null) {
                    getLifecycle().removeObserver(mObserver);
                    mObserver = null;
                }
            }
        });

    }
}

3.3 模擬場景

場景一:點擊綁定按鈕

當界面完全可見的時候,我們點擊“綁定”按鈕,Log輸出為:

點擊綁定按鈕后輸出

場景二:點擊綁定按鈕,點擊解綁按鈕

沒有任何輸出。

場景三:再次點擊綁定按鈕

再次點擊綁定按鈕

場景四:在綁定狀態下,鎖屏或者按 Home 回到桌面

鎖屏或按 Home 回到說面

場景五:解鎖或者點擊圖標重新進入

解鎖或者點擊圖標重新進入

場景六:按 Back 回到桌面

按 Back 回到桌面

場景結論

  • 第一次綁定 的時候,LifeObserver會從onCreate()方法依次回調到Activity當前所處的生命周期狀態。
  • 解綁 不會觸發任何生命周期回調,之后也收不到Activity的任何通知。
  • 在綁定過后,LifeObserver會跟隨著Activity的生命周期變化。

3.4 不使用 AppCompactActivity 實現

假如我們使用的不是AppCompactActivity,那么要怎么達到和3.2中一樣的效果呢,實現方式如下:

  • 讓普通的Activity實現LifecycleOwner接口
  • 在其內部創建一個LifecycleRegistry對象,其構造函數參數為實現了LifecycleOwner接口的ActivitygetLifecycle()方法返回該對象。
  • 通過LifecycleRegistry#addObserver注冊觀察者LiveObserver

整體的實現代碼如下所示:

/**
 * 1.需要讓 Activity 實現 LifecycleOwner 接口。
 */
public class LiveActivity extends Activity implements LifecycleOwner {

    private Button mBtnBind;
    private Button mBtnUnBind;
    //2.創建 mLifecycleRegistry 對象,其構造函數為實現了 LifecycleOwner 的對象。
    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
    private LifecycleObserver mObserver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live);
        mBtnBind = findViewById(R.id.bt_bind);
        mBtnBind.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mObserver == null) {
                    mObserver = new LiveObserver();
                    //4.1 注冊的方法和之前相同。
                    getLifecycle().addObserver(mObserver);
                }
            }
        });
        mBtnUnBind = findViewById(R.id.bt_unbind);
        mBtnUnBind.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (mObserver != null) {
                    //4.2 反注冊的方法和之前相同。
                    getLifecycle().removeObserver(mObserver);
                    mObserver = null;
                }
            }
        });
    }

    /**
     * 3.該函數返回 mLifecycleRegistry。
     *
     * @return 返回 mLifecycleRegistry。
     */
    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
}

其效果和之前3.3中的表現是一致的。

四、實現原理

有沒有感到很神奇,我們是怎么讓一個獨立的對象和Activity/Fragment的生命周期關聯起來的呢。通過源碼我們可以發現,有以下兩個類實現了LifecycleOwner接口:

  • android.support.v4.app.SupportActivity
@RestrictTo(LIBRARY_GROUP)
public class SupportActivity extends Activity implements LifecycleOwner {

    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
   
}
  • android.support.v4.app.Fragment
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner {

    LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
   
}

當我們分析完之后,會發現這兩種使用的是不同的方式來實現我們最終看到的效果的。

4.1 使用 Activity 的 getLifecycle 注冊

當我們使用SupportActivitygetLifecycle(),其實系統在Activity啟動的時候 偷偷 地為我們添加了一個沒有界面的ReportFragment

@RestrictTo(LIBRARY_GROUP)
public class SupportActivity extends Activity implements LifecycleOwner {
    
    @Override
    @SuppressWarnings("RestrictedApi")
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //在 onCreate 方法當中偷偷地添加。
        ReportFragment.injectIfNeededIn(this);
    }

}

用過Fragment的同學們都知道,Fragment的生命周期是和它所在的Activity綁定的,那么當Activity的狀態變化時,Fragment的生命周期就會被回調,我們來看一下ReportFragment是怎么處理的:

public class ReportFragment extends Fragment {

    @Override
    public void onResume() {
        super.onResume();
        //這里會去分發事件。
        dispatch(Lifecycle.Event.ON_RESUME);
    }

    private void dispatch(Lifecycle.Event event) {
        Activity activity = getActivity();
        //這里的 activity 就是我們上面的 SupportActivity,而 getLifecycle 就返回了 LifecycleRegistry 對象。
        if (activity instanceof LifecycleOwner) {
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if (lifecycle instanceof LifecycleRegistry) {
                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
            }
        }
    }
}

接著,就會走到LifecycleRegistryhandleLifecycleEvent方法當中,具體的邏輯如下所示,最終會通知到它內部所持有的LifecycleObserver

public class LifecycleRegistry extends Lifecycle {
    public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
       //獲取下一步的狀態。
       State next = getStateAfter(event);
        //轉移到該狀態。
        moveToState(next);
    }

    private void moveToState(State next) {
        if (mState == next) {
            return;
        }
        mState = next;
        if (mHandlingEvent || mAddingObserverCounter != 0) {
            mNewEventOccurred = true;
            // we will figure out what to do on upper level.
            return;
        }
        mHandlingEvent = true;
        //狀態同步。
        sync();
        mHandlingEvent = false;
    }

    private void sync() {
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            Log.w(LOG_TAG, "LifecycleOwner is garbage collected, you shouldn't try dispatch "
                    + "new events from it.");
            return;
        }
        while (!isSynced()) {
            mNewEventOccurred = false;
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                //1.向后轉移。
                backwardPass(lifecycleOwner);
            }
            Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
            if (!mNewEventOccurred && newest != null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                //2.向前轉移
                forwardPass(lifecycleOwner);
            }
        }
        mNewEventOccurred = false;
    }

    private void forwardPass(LifecycleOwner lifecycleOwner) {
        Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
                mObserverMap.iteratorWithAdditions();
        while (ascendingIterator.hasNext() && !mNewEventOccurred) {
            Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next();
            ObserverWithState observer = entry.getValue();
            while ((observer.mState.compareTo(mState) < 0 && !mNewEventOccurred
                    && mObserverMap.contains(entry.getKey()))) {
                pushParentState(observer.mState);
                //這里的 observer 就是我們通過 addObserver 加入的監聽者,它會根據回調的狀態和注解去調用對應的方法。
                observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState));
                popParentState();
            }
        }
    }
}

整個狀態的傳遞如下圖所示:

狀態傳遞

最后,讓我們用斷點驗證一下之前的分析,以onResume為例:

onResume 方法的調用過程

4.2 使用 Fragment 的 getLifecycle 注冊

下面我們再來分析Fragment的實現過程,Fragment的實現就簡單多了,它是直接在對應的生命周期里,通過內部持有的LifecycleRegistry對象調用的,很好理解:

public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner {

    void performResume() {
        //簡單粗暴,直接通過內部持有的對象調用。
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
    }
}

之后handleLifecycleEvent的過程就和4.1中一致了,就不過多分析了,大家只需要了解基本的思想就好了,不必過于深究源碼。

五、使用 Lifecycle 應該注意什么

https://developer.android.google.cn/topic/libraries/architecture/lifecycle 中提到了下面兩點,屬于對lifecycle組件的總結,但是很多東西,我們還沒有學到,因此先留下原文,等學習完后幾章的內容后,再來理解。

  • Best practices for lifecycle-aware components
  • Use cases for lifecycle-aware components

5.1 Best practices for lifecycle-aware components

  • Keep your UI controllers (activities and fragments) as lean as possible. They should not try to acquire their own data; instead, use a ViewModel to do that, and observe a LiveData object to reflect the changes back to the views.
  • Try to write data-driven UIs where your UI controller’s responsibility is to update the views as data changes, or notify user actions back to the ViewModel.
  • Put your data logic in your ViewModel class. ViewModel should serve as the connector between your UI controller and the rest of your app. Be careful though, it isn't ViewModel's responsibility to fetch data (for example, from a network). Instead, ViewModel should call the appropriate component to fetch the data, then provide the result back to the UI controller.
  • Use Data Binding to maintain a clean interface between your views and the UI controller. This allows you to make your views more declarative and minimize the update code you need to write in your activities and fragments. If you prefer to do this in the Java programming language, use a library like Butter Knife to avoid boilerplate code and have a better abstraction.
  • If your UI is complex, consider creating a presenter class to handle UI modifications. This might be a laborious task, but it can make your UI components easier to test.
  • Avoid referencing a View or Activity context in your ViewModel. If the ViewModel outlives the activity (in case of configuration changes), your activity leaks and isn't properly disposed by the garbage collector.

5.2 Use cases for lifecycle-aware components

  • Switching between coarse and fine-grained location updates. Use lifecycle-aware components to enable fine-grained location updates while your location app is visible and switch to coarse-grained updates when the app is in the background. LiveData, a lifecycle-aware component, allows your app to automatically update the UI when your user changes locations.
  • Stopping and starting video buffering. Use lifecycle-aware components to start video buffering as soon as possible, but defer playback until app is fully started. You can also use lifecycle-aware components to terminate buffering when your app is destroyed.
  • Starting and stopping network connectivity. Use lifecycle-aware components to enable live updating (streaming) of network data while an app is in the foreground and also to automatically pause when the app goes into the background.
  • Pausing and resuming animated drawables. Use lifecycle-aware components to handle pausing animated drawables when while app is in the background and resume drawables after the app is in the foreground.

六、參考文章

(1) Handling lifecycles with lifecycle-aware components
(2) Android 官方架構組件 Lifecycle 詳解 & 原理分析
(3) Android 架構組件(一)——Lifecycle

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容