Android原始碼解析-LiveData

kinsomy發表於2019-02-25

1 概述

LiveData是一個可被觀察的資料持有類,一般的資料類不同,LiveData是生命週期感知的,資料類的生命週期可以和其他app元件的生命週期保持一致,例如Activity,fragment和service。這保證了LiveData僅僅會更新處在活動狀態的元件。

LiveData可以被看成觀察者模式的實踐,LiveData是一個被觀察的物件,其他元件會訂閱對它的觀察,當元件處於Started或者Resumed則為活躍狀態,LiveData只會通知處於這兩者狀態的元件更新。同時希望在元件變為Destroyed狀態時去自動銷燬LiveData和元件的繫結,不至於出現洩漏。

1.1 優勢

  • 確保UI和資料狀態同步

  • 沒有記憶體洩漏

  • 不會因為已經停止的Activity導致crash

  • 不需要手動處理生命週期

  • 元件時刻保持最新資料

  • 支援適當配置更改

    如果一個Activity或者fragment因為例如裝置旋轉而重新建立,它會立即接受到最新可獲得的資料。

  • 共享資源 多個元件可以共享同一份資料

2 實踐

2.1 依賴

LiveData是Android官方架構Jetpack的組成部分,架構元件全部在google的Maven倉庫裡,想要使用可以在專案的build.gradle檔案裡新增google()依賴。

allprojects {
    repositories {
        google() //引入livedata
        jcenter()
    }
}
複製程式碼

在模組的build.gradle檔案中加入lifecycle:extensions依賴

implementation "android.arch.lifecycle:extensions:1.1.1"
複製程式碼

2.2 建立LiveData物件

LiveData通常和Jetpack架構下的另一個元件ViewModel配合使用,ViewModel是一個負責為Activity或者Fragment準備和管理資料的類,同時處理和應用剩餘部分的通訊,注意ViewModel僅僅負責管理UI上的資料,其他都無權干涉,它和元件生命週期繫結,只有Activity結束了,它才會被銷燬。

我們建立一個提供電池電量資訊的ViewModel:

public class BatteryViewModel extends ViewModel {
	private MutableLiveData<Integer> currentBattery;

	public MutableLiveData<Integer> getCurrentBatteryData() {
		if (currentBattery == null) {
			currentBattery = new MutableLiveData<>();
		}

		return currentBattery;
	}
}
複製程式碼

這裡使用了MutableLiveData類來儲存電量資料,它繼承了LiveData,暴露了setValuepostValue方法。

public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }

    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}
複製程式碼

2.3 監聽LiveData物件

寫一個activity模擬顯示電量變化情況,通常我們要在onCreate回撥裡開始監聽LiveData,主要出於以下原因:

  • onCreate是activity建立時的第一個回撥,onResume和onStart在activity生命週期內會回撥多次,造成呼叫監聽多次形成冗餘。
  • 確保activity和fragment在變成活躍狀態進入started時可以儘快獲得資料更新,所以要儘早開始監聽。

LiveData在用非活躍狀態進入活躍狀態時同樣可以接受到更新,但是如果第二次從非活躍狀態進入活躍狀態,那麼只有當上一次變成活躍態的資料發生變化時才會接受更新。

public class BatteryActivity extends AppCompatActivity {
    private int battery = 100;
	private BatteryViewModel mBatteryViewModel;
	@Override
	protected void onCreate(@Nullable Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_livedata_battery);

		//在當前activity範圍內建立或者獲得viewmodel例項
		mBatteryViewModel = ViewModelProviders.of(this).get(BatteryViewModel.class);
		final TextView tvBattery = findViewById(R.id.tv_battery);
		//設定觀察者
		Observer<Integer> batteryOb = new Observer<Integer>() {
			@Override
			public void onChanged(@Nullable Integer integer) {
				tvBattery.setText(String.format("電量:^[0-9]*$", integer));
			}
		};
		//將資料繫結到觀察者
		mBatteryViewModel.getCurrentBatteryData().observe(this, batteryOb);
	}
}
複製程式碼

ViewModelProviders是lifecycle:extensions模組下的類,首先通過of(this)建立一個ViewModelProvider例項,再通過get方法去獲得(如果有)或者建立一個viewmodel,然後建立一個Observer,將它和viewmodel繫結,監聽資料的變化,在onChanged回撥內實時修改UI。

2.4 更新LiveData資料

監聽設定好之後,下面可以通過修改資料看看ui是否被實時修改,點選開始統計按鈕,讓電量減一來模擬掉電情況。

LiveData更改資料的方法不是public型別的,只在內部自己呼叫,所有這裡才會使用MutableLiveData,他暴露了修改資料的公共方法。這裡修改電量減一就是用到了setValue方法。

findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				mBatteryViewModel.getCurrentBatteryData().postValue(battery--);
			}
		});
複製程式碼

可以看到MutableLiveData總共有兩個修改資料的方法,他們的區別是什麼呢?

  • setValue是必須在主執行緒被呼叫,用來修改LiveData資料。
  • postValue可以在後臺執行緒呼叫,它是向執行緒的觀察者傳送一個task,請求修改資料,但是如果在主執行緒執行前呼叫多次,則只有最後一次會生效。

3 擴充套件LiveData

上面是使用自帶的MutableLiveData,同樣也可以自己定義LiveData類。

3.1 自定義LiveData

自定義一個BatteryLiveData類繼承自LiveData,通過廣播去接受系統電池電量,通過setValue將資料設定給LiveData。

public class BatteryLiveData extends LiveData<Integer> {
	private Context context;

	private BroadcastReceiver receiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			setValue(intent.getIntExtra("level", 0));
		}
	};

	public BatteryLiveData(Context context) {
		this.context = context;
	}

	@Override
	protected void onActive() {
		super.onActive();
		IntentFilter filter = new IntentFilter();
		filter.addAction(Intent.ACTION_BATTERY_CHANGED);
		context.registerReceiver(receiver, filter);
	}

	@Override
	protected void onInactive() {
		super.onInactive();
		context.unregisterReceiver(receiver);
	}
}
複製程式碼

需要重寫兩個方法:

  • onActive() 當LiveData繫結有活躍狀態的observer時就會呼叫,在這裡回去註冊廣播獲得電池電量變化。
  • onInactive() 當LiveData沒有任何活躍狀態observer繫結時呼叫,取消註冊廣播。

3.2 獲得資料更新

在activity裡構造LiveData例項,同時呼叫observe()方法將activity和LiveData繫結。

BatteryLiveData batteryLiveData = new BatteryLiveData(this);
		batteryLiveData.observe(this, new Observer<Integer>() {
			@Override
			public void onChanged(@Nullable Integer integer) {
				tvBattery.setText(integer.toString());
			}
		});
複製程式碼

3.3 共享資源

文章一開始講到LiveData的優勢之一就是共享資源,可以將LiveData設計成單例模式,在任何需要的地方呼叫observe()繫結監聽。

public class BatteryLiveData extends LiveData<Integer> {
    ...
	@MainThread
	public static BatteryLiveData getInstance(Context context) {
		if (sInstance == null) {
			sInstance = new BatteryLiveData(context);
		}
		return sInstance;
	}

	private BatteryLiveData(Context context) {
		this.context = context;
	}
    ...
}


BatteryLiveData.getInstance(this).observe(this, new Observer<Integer>() {
			@Override
			public void onChanged(@Nullable Integer integer) {
				tvBattery.setText(integer.toString());
			}
		});
複製程式碼

4 LiveData轉換

4.1 map

map是將一個LiveData轉換成另一個LiveData。

Transformations.map(batteryLiveData, new Function<Integer, String>() {
			@Override
			public String apply(Integer input) {
				return input + "%";
			}
		});
複製程式碼

4.2 switchMap

第二個引數接收一個方法,通過方法將傳入的第一個引數LiveData轉換成另一個LiveData。

		Transformations.switchMap(sInstance, new Function<Integer, LiveData<? extends String>>() {
			@Override
			public LiveData<? extends String> apply(Integer input) {
				return ...;
			}
		});
複製程式碼

5 原始碼解析

下面來分析一下LiveData的關鍵原始碼。先從LiveData類開始: LiveData裡面主要有幾個上面用到的方法,observe,setValue,postValue,onActive,onInactive等,挨個來分析。

5.1 observe()

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

public interface LifecycleOwner {
    @NonNull
    Lifecycle getLifecycle();
}
複製程式碼

observe有兩個引數,第一個是LifecycleOwner,FragmentFragmentActivity等都實現了該介面,因此我們在呼叫的時候就可以直接傳入this,它裡面有一個方法可以獲得當前的Lifecycle例項,Lifecycle裡面儲存了和生命週期相對應的狀態。

observe方法首先先判斷當前狀態是不是DESTROYED,如果是就可以完全忽略,因為已經說過只對處於活躍狀態的元件做更新;接著將owner observer構造成LifecycleBoundObserver例項,這是一個內部類,裡面有關於狀態變換的一系列操作,待會詳細分析;然後將observer和wrapper存入map快取中,如果observer快取已存在並且已經和另一個LifecycleOwner繫結,則丟擲異常;如果快取已經存在則直接忽略;最後呼叫addObserver方法將LifecycleBoundObserver例項和LifecycleOwner繫結。而addObserver是呼叫了LifecycleRegistry類的實現。

5.2 ObserverWrapper

private abstract class ObserverWrapper {
        final Observer<T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;

        ObserverWrapper(Observer<T> observer) {
            mObserver = observer;
        }

        abstract boolean shouldBeActive();

        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }

        void detachObserver() {
        }

        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;
			//過去是inactive,現在是active
            if (wasInactive && mActive) {
                onActive();
            }
			//過去沒有訂閱,並且現在是inactive
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
			//現在是active
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
	...
    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        activeStateChanged(shouldBeActive());
    }
}
複製程式碼

ObserverWrapper裡面封裝了關於狀態的操作,包括判斷是否處於活躍狀態、observer是否繫結到lifecycleowner以及更改activity狀態等。

activeStateChanged首先判斷新來的狀態和舊狀態是否相同,相同則忽略,然後判斷LiveData上的活躍態的數量是否為0,為0說明之前處於Inactive,然後統計現在的訂閱數,接著就是三個if判斷,註釋在程式碼裡。正式這三個判斷,LiveData可以接收到onActive和onInactive的回撥。

dispatchingValue(this)是當狀態變為active時呼叫,用來更新資料。裡面會用到considerNotify方法。

5.3 setValue postValue

private final Runnable mPostValueRunnable = new Runnable() {
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        //noinspection unchecked
        setValue((T) newValue);
    }
};
protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}


@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    //noinspection unchecked
    observer.mObserver.onChanged((T) mData);
}
複製程式碼

setValue會呼叫dispatchingValue方法,接著呼叫considerNotify,在最後呼叫onChange()回撥,就能收到資料變化。

postValue之前說過是往主執行緒傳送事件,同時加鎖保持佔用,防止多執行緒併發競爭導致的資料錯誤,因為每次postValue成功都會對mPendingData重置為NOT_SET。然後想主執行緒傳送Runnable物件,Runnable例項的run方法會執行setValue在主執行緒修改資料。

6 參考資料

相關文章