android JetPack組件之LifeCycle
簡介
Lifecycle組件是JetPack的核心組件之一,JetPack的其他許多組件都會用到Lifecycle這一組件,如ViewModel、LiveData等。LifeCycle是負責觀測Activity生命週期變化狀態,並同步給它的訂閱者,使訂閱者實時感知如Activity生命狀態,在正確的狀態做正確的事,如Activity的onDestroy時,切斷訂閱者與觀測目標的引用鏈,防止內存泄漏等問題
使用方法
我們只需要實現上圖訂閱者接口類,然後在Activity中調用getLifecycle()把訂閱類add進去即可。基礎類LifecycleObserver是一個空接口,如果實現該接口,我們可以任意寫一些方法,在方法頭部寫上註解@OnLifecycleEvent(Lifecycle.Event.ON_xxx)即可,xxx寫生命週期的各個方法,如DESTROY等,當觀測目標生命週期執行onDestroy時就會執行該方法;如下僞代碼:
1. 實現訂閱者
class Subcriber implements LifecycleObser{
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void testOnCreate(){
Log.i("i am running create");
}
}
2. 將訂閱者添加到觀測類裏面去
class MyActivity extends AppCompatActivity{
public void onCreate(savedInstanceState Bundle){
/**
getLifecycle爲Activity已實現的方法
*/
getLifecycle().addObserver(new Subcriber());
}
}
訂閱者還有其他的三個接口如LifecycleEventObserver等,這三個接口已經聲明瞭方法了,我們實現這幾個接口時,只需要在方法內部,判斷方法參數Event事件,然後執行相應的邏輯即可,無須在寫註解事件了;
這裏提出一個問題,如果在Activity中,生命週期已經從onCreate走到了onResume,這時我們在onResume方法裏面才添加一個Subcriber,那麼上面的testOnCreate還會執行嗎?
不知道不要緊,繼續往下看
背後原理
帶着問題閱讀無疑是最好的閱讀,邊閱讀邊思考,這裏提出幾個問題:
- 觀測目標如何實時將自己的狀態同步給訂閱者 ?
- 衆多的訂閱者是如何管理、以及如何同步狀態的 ?
- 觀測目標執行完onDestroy如何處理這些訂閱者 ?
ComponentActivity
文章頂部的圖片,先看看觀測目標的ComponentActivity類,從它入手
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner{
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
}
從上代碼可以看出,對於ComponentActivity的生命週期監聽的類是一個LifecycleRegistry,他負責對生命週期的監聽。
LifecycleRegistry
瞭解添加訂閱者之前,先介紹幾個標誌量:
private State mState; 當前觀測目標狀態、
private int mAddingObserverCounter = 0; 大於0,說明添加訂閱者正在發生
private boolean mHandlingEvent = false; 觀測目標狀態變化了,正在同步給所有的訂閱者
private boolean mNewEventOccurred = false; 以上兩個至少有一個在發生
private ArrayList mParentStates = new ArrayList<>(); 臨時狀態棧,用於添加訂閱者時記錄
然後繼續看看這個如何添加訂閱者的:
public void addObserver(@NonNull LifecycleObserver observer) {
State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
對訂閱者再次封裝包裹,主要是給他添加一個狀態State,用於後續狀態同步及事件分發
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
將訂閱者添加到自己的mObserverMap管理組織起來,後續有事件時從mObserverMap取出來分發事件
mObserverMap是一個HashMap + 雙鏈表的結構,value用雙鏈表組織結構,start和end首尾節點
ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
如果之前已經添加過,就不做後續的工作直接返回,
if (previous != null) {
return;
}
mLifecycleOwner爲當前LifecycleRegistry構造方法傳入的觀測目標的弱引用,
如果弱引用都沒了,就說明觀測目標都不存在了
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
if (lifecycleOwner == null) {
// it is null we should be destroyed. Fallback quickly
return;
}
mAddingObserverCounter > 0表示正在爲傳入的observer做同步操作,可能添加進來時,觀測目標
的聲明週期走了好幾個了,要給他補上
mHandlingEvent爲true表明聲明週期事件觸發了,正在給他下面的訂閱者進行事件分發
boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
獲取observer的前一個節點,與當前觀測目標對比,取小值
State targetState = calculateTargetState(observer);
mAddingObserverCounter++;
mAddingObserverCounter > 0表示正在給當前observer補上他的生命週期
狀態對比statefulObserver.mState.compareTo(targetState) < 0,這個說明當前observer狀態
比現在狀態還小,他需要走upEvent事件
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
對當前狀態壓棧
pushParentState(statefulObserver.mState);
給這個observer分發事件,upEvent是從獲取他的up事件,也就是從狀態切換到事件
statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
popParentState();
//再次選最小值,知道while不滿足,也就是和當前狀態相等
targetState = calculateTargetState(observer);
}
if (!isReentrance) {
// we do sync only on the top level.
sync();
}
mAddingObserverCounter--;
}
//訂閱者封裝包裹類
static class ObserverWithState {
State mState;
LifecycleEventObserver mLifecycleObserver;
ObserverWithState(LifecycleObserver observer, State initialState) {
lifecycleEventObserver將其轉換爲LifecycleEventObserver類型
mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
mState = initialState;
}
void dispatchEvent(LifecycleOwner owner, Event event) {
State newState = getStateAfter(event);
mState = min(mState, newState);
這裏事件分發
mLifecycleObserver.onStateChanged(owner, event);
mState = newState;
}
}
計算它前一個Observer和當前觀測目標的聲明週期狀態,取最小值
private State calculateTargetState(LifecycleObserver observer) {
ceil取他前面的一個observer節點
Entry<LifecycleObserver, ObserverWithState> previous = mObserverMap.ceil(observer);
State siblingState = previous != null ? previous.getValue().mState : null;
mParentStates是臨時壓棧的狀態,也是在observer前面一個
State parentState = !mParentStates.isEmpty() ? mParentStates.get(mParentStates.size() - 1)
: null;
返回最小
return min(min(mState, siblingState), parentState);
}
以上代碼,主要完成以下工作:
- 將訂閱者LifeCycleObserver封裝爲ObserverWithState類,包含一個狀態變量和統一的事件分發方法
- 將ObserverWithState添加到自己的mObserverMap中去,mObserverMap爲FastSafeIterableMap類型,是一個HashMap+雙鏈表結構
- 檢查觀測目標與這個LifeCycleObserver狀態,如果不同,要同步一下LifeCycleObserver到現在的狀態
對訂閱者的數據結構管理
在LifecycleRegistry中,mObserverMap是管理訂閱者FastSafeIterableMap<LifecycleObserver, ObserverWithState>的類,這個類的結構是HashMap+雙鏈表,mObserverMap是FastSafeIterableMap類型,看看他的結構體如何:
public class FastSafeIterableMap<K, V> extends SafeIterableMap<K, V> {
private HashMap<K, Entry<K, V>> mHashMap = new HashMap<>();
public V putIfAbsent(@NonNull K key, @NonNull V v) {
調用父類的get檢查是否已經添加過
Entry<K, V> current = get(key);
if (current != null) {
return current.mValue;
}
在加入到HashMap中去
mHashMap.put(key, put(key, v));
return null;
}
}
public class SafeIterableMap<K, V> implements Iterable<Map.Entry<K, V>> {
Entry<K, V> mStart;
private Entry<K, V> mEnd;
遍歷查找是否有k的value
protected Entry<K, V> get(K k) {
Entry<K, V> currentNode = mStart;
while (currentNode != null) {
if (currentNode.mKey.equals(k)) {
break;
}
currentNode = currentNode.mNext;
}
return currentNode;
}
新來的數據添加到mEnd之後,所以start是最新添加的,end是最後添加
protected Entry<K, V> put(@NonNull K key, @NonNull V v) {
Entry<K, V> newEntry = new Entry<>(key, v);
mSize++;
if (mEnd == null) {
mStart = newEntry;
mEnd = mStart;
return newEntry;
}
mEnd.mNext = newEntry;
newEntry.mPrevious = mEnd;
mEnd = newEntry;
return newEntry;
}
原始的存儲element,多了上下兩個指向變量
static class Entry<K, V> implements Map.Entry<K, V> {
@NonNull
final K mKey;
@NonNull
final V mValue;
Entry<K, V> mNext;
Entry<K, V> mPrevious;
}
}
小結:
很多細節寫到了代碼註釋裏面去了,但是這裏爲什麼要使用HashMap+雙鏈表呢?只用其中一個不行嗎?答案肯定是可以的,但是爲了效率問題,在同步訂閱者狀態時,需要不停的檢測是否contains(observer),用hashMap效率快點,而隊列則要全部遍歷
生命狀態同步
Lifecycle將生命週期劃分爲 狀態 和 事件 來處理,如下枚舉:
public enum Event {
ON_CREATE,
ON_START,
ON_RESUME,
ON_PAUSE,
ON_STOP,
ON_DESTROY,
ON_ANY //能匹配任何狀態
}
public enum State {
DESTROYED,
INITIALIZED,
CREATED,
RESUMED;
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
}
而且每個狀態執行了不同的方法會變成另一個狀態,狀態與事件的切換圖如下:
在LifecycleRegistry中,將狀態同步到訂閱者時,就是根據上面圖來對比執行的;假如某個訂閱者A的狀態是INITIALIZED,而觀測目標B現在是CREATED狀態,說明A比B的狀態小,說明是上面的事件,對應代碼就是upEvent方法,返回ON_CREATE事件,這個時候就把ON_CREATE事件同步給A,如下代碼:
private void sync() {
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
if (lifecycleOwner == null) {
throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
+ "garbage collected. It is too late to change lifecycle state.");
}
while (!isSynced()) {
mNewEventOccurred = false;
如果最早的訂閱者比觀測目標大,說明是downEvent事件,就倒序遍歷backwardPass
if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
backwardPass(lifecycleOwner);
}
同上
Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
if (!mNewEventOccurred && newest != null
&& mState.compareTo(newest.getValue().mState) > 0) {
forwardPass(lifecycleOwner);
}
}
mNewEventOccurred = false;
}
private void backwardPass(LifecycleOwner lifecycleOwner) {
遞減iterator
Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
mObserverMap.descendingIterator();
開始遍歷,而且沒有新事件狀態下來
while (descendingIterator.hasNext() && !mNewEventOccurred) {
Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next();
ObserverWithState observer = entry.getValue();
取出訂閱者ObserverWithState
while ((observer.mState.compareTo(mState) > 0 && !mNewEventOccurred
&& mObserverMap.contains(entry.getKey()))) {
獲取狀態的down事件,狀態 --> 事件
Event event = downEvent(observer.mState);
pushParentState(getStateAfter(event));
將event分發到observer
observer.dispatchEvent(lifecycleOwner, event);
popParentState();
}
}
}
小結:
重點在於理解狀態事件遷移圖,就可以理解代碼是如何將狀態切換的,以及如何分發下去;sync方法的意思就是:
- 首先對比訂閱者集合中首尾節點狀態是否一致,並且和觀測目標當前是否一致,不一致說明需要進行狀態同步
- 先判斷最早的訂閱者節點是否小於觀測目標狀態,小於就從訂閱者集合倒序遍歷進行狀態同步,只有小於觀測目標狀態纔會同步backwardPass
- 然後判斷最後的節點狀態是否大於觀測目標狀態,大於就正序遍歷forwardPass所有訂閱者節點,大於就進行事件分發
- 走完一遍後,在執行步驟1,如果狀態還不一致,在執行2和3,因爲有的訂閱者可能和觀測目標之間相隔1個以上的狀態,所以需要再次執行while循環
- 最後的效果是,所有訂閱者狀態一步一步向觀測目標的狀態靠攏,而不是一個訂閱者連續執行兩個狀態事件
Activity的生命週期事件如何傳遞進入LifecycleRegistry
簡單來說,只需要在Activity的各個生命週期調用getLifecycle.handleLifecycleEvent()即把生命狀態傳遞進去了,遺憾的是我在ComponentActivity類裏面並沒有看到生命週期去調用handleLifecycleEvent方法,很奇怪。找遍他的子類也沒有,但是訂閱者卻能響應各個生命週期方法,肯定是哪裏調用了的,只是我沒有找到,如果讀者的你找到,還望分享一下!
總結
Lifecycle是Jetpack的核心組件之一,使用很方便,能讓你的訂閱者時刻感知目標的狀態;重點要理解Lifecycle如何管理衆多的訂閱者,生命狀態如何在訂閱者之間同步,以及狀態遷移;看代碼的你可能發現了這個問題,LifecycleRegistry添加訂閱者後,並沒有在OnDestroy後把這些訂閱者remove掉,會不會導致內存泄漏,答案是否定的,因爲LifecycleRegistry構造方法傳遞進去的是弱引用,不影響GC對Activity的垃圾回收