Android跨介面共享資料——LiveData應用

唐子玄發表於2018-12-10

業務場景

ezgif.com-video-to-gif.gif

3個介面中有3個獨立控制元件,需要同步他們的狀態,即其中任一控制元件狀態變化,其餘兩個隨之而變。

解決方案

1. 傳遞值:startActivityForResult() + onActivityResult()

這是最容易想到的方案,實現步驟如下:

  • 在介面A將控制元件狀態封裝在Intent
  • 在介面A通過startActivityForResult()跳轉到介面B
  • 在介面A返回之前通過setResult()將控制元件狀態返回給介面A
  • 在介面A的onActivityResult()中獲取控制元件狀態並更新UI

但該方案有缺點:

  1. 程式碼可讀性較差,特別是當onActivityResult()中還夾雜著其他業務邏輯。
  2. 增加了Activity間的耦合(即Activiy B依賴於Activity A的特殊傳值方式,Activity A依賴於Activity B的回傳值)。因為介面間是兩兩耦合的,所以也導致了擴充套件性較差,如果需求改成“從Activity A直接跳轉到Activity B”,需要重新出處理Activity AActivity 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

拋磚引玉,若大家有更好的方案,歡迎交流~~

相關文章