[譯] Architecture Components 之 Handling Lifecycles

zly394發表於2017-06-08

【目錄】

1. Architecture Components 之 Guide to App Architecture

2. Architecture Components 之 Adding Components to your Project

3. Architecture Components 之 Handling Lifecycles

4. Architecture Components 之 LiveData

5. Architecture Components 之 ViewModel

6. Architecture Components 之 Room Persistence Library

示例程式碼連結


處理生命週期

android.arch.lifecycle 包提供了可以用於構建生命週期感知(lifecycle-aware)元件的類和介面,這些元件可以根據 Activity 或 Fragment 的當前生命週期自動調整其行為。

注:匯入 android.arch.lifecycle 到專案中請參看新增元件到專案

Android Framework 中定義的大多數應用程式元件都附有生命週期。這些生命週期由作業系統或在程式中執行的框架程式碼管理。它們是 Android 執行的核心並且應用程式必須遵守它們,不這樣做可能會導致記憶體洩漏甚至應用程式崩潰。

假設我們有一個需要在螢幕上顯示裝置位置的 Activity。常見的實現方式可能如下所示:

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // 連結到系統定位服務
    }

    void stop() {
        // 斷開系統定位服務
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // 更新 UI
        });
  }

    public void onStart() {
        super.onStart();
        myLocationListener.start();
    }

    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}複製程式碼

儘管這個例子看起來不錯,在真正的應用程式中,你最終會有太多的類似呼叫並且會導致 onStart() 和 onStop() 方法變的非常臃腫。

另外,一些元件不能在 onStart() 方法中立即啟動。如果我們需要在啟動位置觀察者之前檢查一些配置怎麼辦?在某些情況下很可能發生 Activity 停止後才檢查配置完成,這意味著在 myLocationListener.stop() 被呼叫之後 myLocationListener.start() 才被呼叫,會導致定位服務基本上永遠保持連線。

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // 更新 UI
        });
    }

    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // 如果這個回撥在 Activity 停止後被呼叫怎麼辦?
            if (result) {
                myLocationListener.start();
            }
        });
    }

    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}複製程式碼

android.arch.lifecycle 包提供了類和介面幫助以彈性和隔離的方式解決這些問題。

Lifecycle

Lifecycle 是一個類,它持有關於元件(如 Activity 或 Fragment)生命週期狀態的資訊,並且允許其他物件觀察此狀態。

Lifecycle 使用兩個主要的列舉來跟蹤其關聯元件的生命週期狀態。

Event

生命週期事件是從框架和 Lifecycle 發出的事件。這些事件對映到 Activity 和 Fragment 中的回撥事件。

State

Lifecycle 物件跟蹤的元件的當前狀態。

[譯] Architecture Components 之 Handling Lifecycles

將狀態視為圖表的節點,事件作為這些節點之間的邊緣。

類可以通過向其方法新增註釋來監控元件的生命週期狀態。

public class MyObserver implements LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void onPause() {
    }
}
aLifecycleOwner.getLifecycle().addObserver(new MyObserver());複製程式碼

LifecycleOwner

LifecycleOwner 是一個單一方法的介面,它表示實現類具有一個 Lifecycle。它有一個 getLifecycle()) 方法,該方法必須由實現類實現。

該實現類從個別的類(例如 Activity 和 Fragment)提取生命週期的所有權,並且允許編寫可與兩者相容的元件。任何自定義應用程式類都可以實現 LifecycleOwner 介面。

注:由於 Architecture Components 處於 alpha 階段,所以 FragmentAppCompatActivity 不能實現 LifecycleOwner (因為我們不能在穩定的元件中新增依賴不穩定的API)。在 Lifecycle 穩定之前,為了方便提供了 LifecycleActivityLifecycleFragment 類。在 Lifecycles 專案釋出後,支援庫中的 Fragment 和 Activity 將會實現 LifecycleOwner 介面;屆時 LifecycleActivityLifecycleFragment 將會被棄用。另請參閱在自定義 Activity 和 Fragment 中實現 LifecycleOwner

對於上面的例子,我們可以使 MyLocationListener 類成為 LifecycleObserver 然後在 onCreate 中使用 Lifecycle 初始化它。這樣讓 MyLocationListener 類自給自足,意味著在必要的時候它能對自己進行清理。

class MyActivity extends LifecycleActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // 更新 UI
        });
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
  }
}複製程式碼

一個常見的用例是避免在 Lifecycle 處於不良狀態時呼叫某些回撥。例如,如果在儲存 Activity 狀態後回撥執行 Fragment 事務,將會導致崩潰,因此我們永遠不會想要呼叫該回撥。

為了簡化該用例, Lifecycle 類允許其他物件查詢當前狀態。

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) {
           // 連線
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getState().isAtLeast(STARTED)) {
            // 如果沒有連線則進行連線
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void stop() {
        // 如果已經連線則斷開連線
    }
}複製程式碼

通過這種實現,LocationListener 類是完全的生命週期感知(lifecycle-aware);它可以進行自己的初始化或清理操作,而不受其 Activity 的管理。如果需要在其它的 Activity 或其他的 Fragment 中使用 LocationListener,只需要初始化它。所有的安裝和解除安裝操作都由類自己管理。

可以與 Lifecycle 一起使用的類稱為生命週期感知(lifecycle-aware) 元件。鼓勵有需要使用 Android 生命週期的類的庫提供生命週期感知(lifecycle-aware) 元件,以便於使用者可以輕鬆的在客戶端整合這些類,而不需要手動管理生命週期。

LiveData 是一個生命週期感知(lifecycle-aware) 元件的示例。與 ViewModel 一起使用 LiveData 可以在遵守 Android 生命週期的前提下,更容易地使用資料填充UI。

Lifecycles 的最佳實踐

  • 保持 UI 控制器(Activity 和 Fragment)儘可能的精簡。它們不應該試圖去獲取它們所需的資料;相反,要用 ViewModel 來獲取,並且觀察 LiveData 將資料變化反映到檢視中。

  • 嘗試編寫資料驅動(data-driven)的 UI,即 UI 控制器的責任是在資料改變時更新檢視或者將使用者的操作通知給 ViewModel

  • 將資料邏輯放到 ViewModel 類中。ViewModel 應該作為 UI 控制器和應用程式其它部分的連線服務。注意:不是由 ViewModel 負責獲取資料(例如:從網路獲取)。相反,ViewModel 呼叫相應的元件獲取資料,然後將資料獲取結果提供給 UI 控制器。

  • 使用 Data Binding 來保持檢視和 UI 控制器之間的介面乾淨。這樣可以讓檢視更具宣告性,並且儘可能減少在 Activity 和 Fragment 中編寫更新程式碼。如果你喜歡在 Java 中執行該操作,請使用像 Butter Knife 這樣的庫來避免使用樣板程式碼並進行更好的抽象化。

  • 如果 UI 很複雜,可以考慮建立一個 Presenter 類來處理 UI 的修改。雖然通常這樣做不是必要的,但可能會讓 UI 更容易測試。

  • 不要在 ViewModel 中引用 View 或者 Activity 的 context。因為如果 ViewModel 存活的比 Activity 時間長(在配置更改的情況下),Activity 將會被洩漏並且無法被正確的回收。

附錄

在自定義 Activity 和 Fragment 中實現 LifecycleOwner

任何自定義的 Fragment 或 Activity 都可以通過實現內建的 LifecycleRegistryOwner 介面轉換為 LifecycleOwner(而不是繼承 LifecycleActivityLifecycleFragment

public class MyFragment extends Fragment implements LifecycleRegistryOwner {
    LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);

    @Override
    public LifecycleRegistry getLifecycle() {
        return lifecycleRegistry;
    }
}複製程式碼

如果要建立一個 LifecycleOwner 的自定義類,可以使用 LifecycleRegistry 類,但是需要將事件轉發到該自定義類中。如果是 Fragment 和 Activity 實現了 LifecycleRegistryOwner 介面,則此轉發會自動完成。

相關文章