業務場景
3個介面中有3個獨立控制元件,需要同步他們的狀態,即其中任一控制元件狀態變化,其餘兩個隨之而變。
解決方案
1. 傳遞值:startActivityForResult() + onActivityResult()
這是最容易想到的方案,實現步驟如下:
- 在介面A將控制元件狀態封裝在
Intent
中 - 在介面A通過
startActivityForResult()
跳轉到介面B - 在介面A返回之前通過
setResult()
將控制元件狀態返回給介面A - 在介面A的
onActivityResult()
中獲取控制元件狀態並更新UI
但該方案有缺點:
- 程式碼可讀性較差,特別是當
onActivityResult()
中還夾雜著其他業務邏輯。 - 增加了
Activity
間的耦合(即Activiy B
依賴於Activity A
的特殊傳值方式,Activity A
依賴於Activity B
的回傳值)。因為介面間是兩兩耦合的,所以也導致了擴充套件性較差,如果需求改成“從Activity A
直接跳轉到Activity B
”,需要重新出處理Activity A
到Activity B
的跳轉邏輯。
2. 共享值(持久化)
既然通過傳遞值的方式不夠好,那直接“共享值”呢?即將每次狀態改變都持久化(存在本地),每次繪製介面都從本地讀取狀態。
設想介面A中有一個列表,每個表項都包含一個需要狀態同步的控制元件,當伺服器返回一批新資料後,需要挨個將資料進行儲存,隨著列表不斷重新整理,本地儲存的內容就不斷增多,為控制本地儲存佔用的空間,在 App 退出時需清空本地儲存。
3. 共享值(LiveData)
既然在 App 退出時需要清空資料,則表明控制元件狀態資訊的生命週期和 App 的生命週期同步,而持久化解決的問題是生命週期長於 App 生命週期的情況。於是第三個解決方案就閃亮登場了~~~
LiveData
是谷歌在Google I/O 2017釋出的Android Architecture Components(Google教你如何寫 App 系列)中的一項內容。
對於當前這個case,LiveData
充當如下角色:
LiveData
是一個資料持有者,但不像一般的資料持有者,它可以感知系統元件的生命週期。LiveData
可以被觀察,但它不像一般的觀察者模式(一有資料變動就通知所有觀察者)。只有當被觀察者處於啟用狀態時才被通知。
所以基於LiveData
的解決方案如下:將控制元件狀態資訊儲存在LiveData
中,三個不同的介面分別觀察LiveData
。
通過觀察者模式將方案1中資料傳遞問題轉換為資料共享,三個介面沒有絲毫耦合。將LiveData設定為單例,使其和 App 生命週期相一致,也避免了開闢額外的本地儲存。
LiveData應用
1. 建立狀態資訊實體類
將要共享的狀態資訊封裝成實體類,簡單起見,demo將狀態資訊設定為int
值,如下:
public class Status {
private int level;
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
}
複製程式碼
2. 建立LiveData單例
下面的程式碼只是將狀態資訊實體類和LiveData
關聯,並將LiveData
定義為單例,方便跨介面使用。
public class StatusLiveData extends MutableLiveData<Status> {
private StatusLiveData() {
}
private static class Holder {
public static final StatusLiveData INSTANCE = new StatusLiveData();
}
public static StatusLiveData getInstance() {
return Holder.INSTANCE;
}
}
//MutableLiveData在LiveData基礎上暴露兩個設值介面
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);
}
}
複製程式碼
3. 為LiveData新增觀察者
LiveData
的觀察者通常是帶有生命週期概念的元件,比如Activity,Fragment等等。觀察者需實現Observer<T>
介面,以定義資料變化時做出的響應。
public class ActivityA extends AppCompatActivity implements View.OnClickListener, Observer<Status> {
private int level;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
StatusLiveData.getInstance().observe(this, this);
}
...
@Override
public void onChanged(@Nullable Status status) {
/**
* get status data when it is changed and update UI
*/
int level = status.getLevel();
changeArrowStatus(level);
}
}
複製程式碼
4. 更新LiveData
最後一步就是在狀態值變化時候呼叫LiveData.setValue()
更新資料。這裡的邏輯和具體業務相關,demo中的業務場景是點選ImageView
控制元件時改變其圖片。
public class ActivityB extends AppCompatActivity implements View.OnClickListener, Observer<Status> {
private int level;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
findViewById(R.id.iv_arrow).setOnClickListener(this);
...
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
...
case R.id.iv_arrow:
changeArrowStatus(++level);
putStatus(level);
break;
}
}
/**
* put status data into LiveData when data is changed
*/
private void putStatus(int level) {
Status status = new Status();
status.setLevel(level);
StatusLiveData.getInstance().setValue(status);
}
private void changeArrowStatus(int level) {
ImageView ivArrow = findViewById(R.id.iv_arrow);
LevelListDrawable levelListDrawable = ((LevelListDrawable) ivArrow.getDrawable());
levelListDrawable.setLevel(level % 2);
}
}
複製程式碼
talk is cheap, show me the code
拋磚引玉,若大家有更好的方案,歡迎交流~~