1. 程式人生 > >Android Lifecycle詳解(一)

Android Lifecycle詳解(一)

官方文件翻譯

使用生命週期感知元件處理生命週期

生命週期感知元件響應於另一元件的生命週期狀態(如Activity和Fragment)的變化而執行動作。這些元件有助於產生更好的組織性和更輕的重量程式碼,這更易於維護。

一種常見的模式是在Activity和Fragment的生命週期方法中實現依賴元件的動作。然而,這種模式導致程式碼的組織和錯誤擴散。通過使用生命週期感知元件,可以將依賴元件的程式碼從生命週期方法中移入元件本身。

android.arch.lifecycle包提供了類和介面,這些類和介面允許您構建生命週期感知元件,這些元件可以根據Activity或Fragment的當前生命週期狀態自動調整其行為。

Lifecycle的依賴,包括LiveData和 ViewModel。

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" // use kapt for Kotlin // 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" }

AndroidX

dependencies {
    def lifecycle_version = "2.0.0-beta01"

    // ViewModel and LiveData
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    // alternatively - just ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" // use -ktx for Kotlin
    // alternatively - just LiveData
    implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
    // alternatively - Lifecycles only (no ViewModel or LiveData). Some UI
    //     AndroidX libraries use this lightweight import for Lifecycle
    implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"

    annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" // use kapt for Kotlin
    // alternately - if using Java8, use the following instead of lifecycle-compiler
    implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

    // optional - ReactiveStreams support for LiveData
    implementation "androidx.lifecycle:lifecycle-reactivestreams:$lifecycle_version" // use -ktx for Kotlin

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

在Android框架中定義的大多數應用程式元件都有生命週期。生命週期是由作業系統或執行在您的程序中的框架程式碼管理的。它們是Android工作的核心,你的應用程式必須依賴於它們。不這樣做可能觸發記憶體洩漏或甚至應用崩潰。

我們有一個Activity用於顯示螢幕上的裝置位置。一個常見的實現方式可能如下:

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

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}


class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

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

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

儘管這個示例看起來很好,但在實際應用程式中,由於響應生命週期的當前狀態,最終有太多的呼叫用於管理UI和其他元件。管理多個元件在生命週期方法中放置相當數量的程式碼,例如onStart()onStop(),這使得它們難以維護。

此外,不能保證元件在activity 或者 fragment 停止之前啟動。如果我們需要執行一個長時間執行的操作,如在onStart()中執行的一些配置的檢查,則尤其如此。這會導致onStop()方法在onStart()方法之前完成執行的競爭條件,使元件活的時間比它需要的時間更長。

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

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

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start();
            }
        });
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

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

Lifecycle

Lifecycle是一個包含元件生命週期狀態(如activity或fragment)資訊的類,並允許其他物件觀察該狀態。

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

Event

從框架和Lifecycle類中分派的生命週期事件。這些事件對映到activities和fragments中的回撥事件。

State

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

把狀態看作圖和事件的節點,作為這些節點之間的邊緣。

一個類可以通過向其方法添加註解來監視元件的生命週期狀態。然後,您可以通過呼叫Lifecycle類的addObserver()方法新增一個觀察者,並傳遞觀察者的例項,如下面的示例所示:
image

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

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void disconnectListener() {
        ...
    }
}

myLifecycleOwner.getLifecycle().addObserver(new MyObserver());

在上面的示例中,myLifecycleOwner物件實現LifecycleOwner介面,這在下面的章節中解釋。

LifecycleOwner

LifecycleOwner是一個單一的方法介面,表示該類具Lifecycle。它有一個方法,getLifecycle(),它必須被實現。如果您試圖管理整個應用程式的生命週期,請參閱ProcessLifecycleOwner

該介面從單個類(如FragmentAppCompatActivity)抽象出Lifecycle的所有權,並允許編寫與其共同工作的元件。任何自定義應用程式類都可以實現LifecycleOwner介面。

實現LifecycleObserver的元件與實現LifecycleOwner的元件無縫協作,因為所有者可以提供一個生命週期,觀察者可以註冊來監視該生命週期。

對於位置跟蹤示例,我們可以使MyLocationListener類實現LifecycleObserver,然後在activity的LifecycleonCreate()方法中初始化它。這允許MyLocationListener類自給自足,意味著對生命週期狀態的變化做出反應的邏輯在MyLocationListener中定義而不是activity。讓各個元件儲存它們自己的邏輯,使得activities和fragments邏輯更易於管理。

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // update UI
        });
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
  }
}

如果Lifecycle現在不處於一個合適的狀態,常見的做法是避免呼叫某些回撥。例如,如果一個回撥函式執行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) {
           // 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
    }
}

通過這個實現,我們的LocationListener類完全是生命週期感知的。如果我們需要從另一個activity或fragment中使用我們的LocationListener,我們只需要初始化它。所有的設定和拆卸操作都是由類本身管理的。

如果一個庫提供的類需要與Android lifecycle 一起使用,我們建議您使用生命週期感知元件。您客戶端的lib可以輕鬆地將這些元件整合在客戶端上而無需手動管理生命週期。

實現一個自定義的LifecycleOwner

支援庫26.1.0中的Fragment和Activities已經實現了LifecycleOwner介面.

如果您有一個自定義類,您想建立一個LifecycleOwner,您可以使用LifecycleRegistry類,但是您需要將事件轉發到該類中,如下面的程式碼示例所示:

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry mLifecycleRegistry;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mLifecycleRegistry = new LifecycleRegistry(this);
        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        mLifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

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

生命週期感知元件的最佳實踐

保持UI控制器(activities和fragments)儘可能輕量級。他們不應該試圖獲取他們自己的資料;相反,使用ViewModel來做這件事,並觀察LiveData物件,以將資料變化返回給檢視。

嘗試編寫資料驅動的UI,其中UI控制器的職責是在資料更改時更新檢視,或將使用者操作通知回ViewModel。

將你的資料邏輯放在ViewModel類中。ViewModel應充當UI控制器和應用程式其餘部分之間的聯結器。但是要注意,ViewModel的責任不是用來獲取資料(例如,從一個網路獲取資料)。相反,ViewModel應該呼叫適當的元件來獲取資料,然後將結果提供給UI控制器。

使用Data Binding來維護View和UI控制器之間的乾淨介面。這可以讓您的檢視更具宣告性,並最小化您需要在activities和fragments中寫入的更新程式碼。如果你習慣用Java程式語言做這件事,使用像Butter Knife這樣的庫來避免樣板程式碼並有更好的抽象。

如果UI複雜,請考慮建立一個presenter類來處理UI修改。這可能是一項費力的任務,但它可以使UI元件更容易測試。

避免在ViewModel中引用View或者Activity的上下文。如果ViewModel超出了activity(在配置改變的情況下),那麼您的activity就會記憶體洩漏,並且不能被垃圾收集器正確地處理。

生命週期感知元件的使用場景

  • 生命週期感知元件可以使您在各種情況下更容易管理生命週期。舉幾個例子:

  • 在粗粒度和細粒度位置更新之間切換。使用生命週期感知的元件,使細粒度的位置更新,而您的位置應用程式是可見的,並切換到粗粒度更新時,應用程式是在後臺。LiveData是一個生命週期感知元件,允許你的app自動更新UI,在使用者更改位置的時候。

  • 停止和啟動視訊緩衝。使用生命週期感知的元件儘快啟動視訊緩衝,但推遲播放直到應用程式完全啟動。當應用程式被銷燬時,還可以使用生命週期感知元件來終止緩衝。

  • 啟動和停止網路連線。當應用程式處於前臺,使用生命週期感知的元件來啟用網路資料的實時更新(流),並且當應用程式進入後臺時也會自動暫停。

  • 停止和恢復動畫drawables。當應用程式處於後臺時,使用生命週期感知的元件來停止動畫,當應用程式處於前臺之後恢復動畫播放。

停止事件處理

Lifecycle屬於AppCompatActivityFragment時,生命週期的狀態將更改為CREATED,而當AppCompatActivity或Fragment的onSaveInstanceState()被呼叫時,ON_STOP事件將被排程。

當通過onSaveInstanceState()儲存Fragment或AppCompatActivity的狀態時,其UI被認為是不可變的,直到呼叫ON_START。試圖在儲存狀態後修改UI可能會導致應用程式導航狀態的不一致,這是為什麼如果在儲存狀態後應用程式執行FragmentTransaction,則FragmentManager丟擲異常。有關詳細資訊,請參閱commit()

LiveData阻止了它的邊緣情況,通過阻止呼叫observer,如果observer關聯的Lifecycle不是在STARTED狀態。在幕後,它呼叫isAtLeast(),然後決定呼叫它的observer。

不幸的是,AppCompatActivity的onStop()方法是在onSaveInstanceState()之後呼叫的,它留下了一個不允許UI狀態改變的間隙,但是Lifecycle還沒有被移動到建立的狀態。

為了防止這種情況,BETA2版本以及更低版本標記這個狀態為CREATED,並且沒有分發這個事件,以至於任何程式碼段檢查當前的狀態來獲取真正的值,儘管事件在 onStop()被呼叫的時候 才會被分發。

不幸的是,這個解決方案有兩個主要問題:

  • 在API級別23和更低的情況下,Android系統實際上儲存了活動的狀態,即使它被另一個activity部分覆蓋。換句話說,Android系統呼叫onSaveInstanceState(),但它不一定呼叫onStop()。這就形成了一個潛在的長間隔,以至於觀察者仍然認為生命週期是活動的,而此時它的UI狀態不能被修改。

  • 任何想要向LiveData類暴露類似行為的類都必須實現Lifecycle Beta2版本或者更低版本提供的解決方案。

注意: 為了使流程更簡單,並與舊版本提供更好的相容性,從版本1.0.0 RC1開始,Lifecycle物件被標記為CREATED,當onSaveInstanceState()被呼叫時, ON_STOP事件被分發,而不需等待呼叫onStop()方法。這不太可能影響您的程式碼,但這裡你需要注意,因為它與API級別26和更低的Activity類中的呼叫順序不匹配。

附加資源

嘗試lifecycle 元件 codelab.

生命週期感知元件是Android Jetpack的一部分。在Sunflower演示應用程式中可以看到他們。