在Android中使用LiveData和ViewModel

1、概述

LiveData是一種可觀察的數(shù)據(jù)存儲(chǔ)器類(lèi),LiveData使用觀察者模式,每當(dāng)數(shù)據(jù)發(fā)生變化時(shí),LiveData會(huì)通知 Observer對(duì)象,我們可以在這些 Observer 對(duì)象中更新UI

ViewModel對(duì)象為特定的界面組件(如 Fragment 或 Activity)提供數(shù)據(jù),并包含數(shù)據(jù)處理業(yè)務(wù)邏輯,會(huì)配合LiveData一起使用

接下來(lái),我們會(huì)先介紹如果使用LiveData,并編寫(xiě)一個(gè)LiveData Demo,接著再結(jié)合ViewModel對(duì)LiveData Demo的代碼進(jìn)行重構(gòu)

2、LiveData使用說(shuō)明

LiveData<T>是一個(gè)抽象類(lèi),它有2個(gè)子類(lèi)分別是:MutableLiveData<T>MediatorLiveData<T>,在編寫(xiě)代碼時(shí),是創(chuàng)建的子類(lèi)。我們先來(lái)看MutableLiveData<T>使用方法,在后面的示例中再介紹如何使用MediatorLiveData<T>

2.1、創(chuàng)建LiveData對(duì)象

如果我們要觀察的對(duì)象類(lèi)為String,就通過(guò)如下代碼創(chuàng)建一個(gè)MutableLiveData對(duì)象

MutableLiveData<String> liveData = new MutableLiveData<>();

2.2、觀察LiveData對(duì)象

通過(guò)observe方法來(lái)監(jiān)聽(tīng)LiveData的數(shù)據(jù)變化,每當(dāng)LiveData發(fā)生變化時(shí),都會(huì)回調(diào)onChanged方法,并返回值的內(nèi)容String s

liveData.observe(this, new Observer<String>() {
    @Override
    public void onChanged(String s) {
        Log.i(TAG, "onChanged: " + s);
    }
});

2.3、更新LiveData對(duì)象

通過(guò)LiveDatasetValue方法,給LiveData賦值,每次賦完值后,都會(huì)回調(diào)onChanged方法

liveData.setValue("add for test");

當(dāng)給LiveData賦值為"add for test"時(shí),onChanged會(huì)輸出日志:

2021-03-20 21:59:21.483 26430-26430/com.example.livedatademo I/LiveDataDemo: onChanged: add for test

3、編寫(xiě)LiveData Demo

我們創(chuàng)建一個(gè)Demo,主界面只有一個(gè)TextView,TextView用作展示一個(gè)數(shù)字,這個(gè)數(shù)字會(huì)從59開(kāi)始顯示,然后每隔1秒數(shù)字會(huì)減1,直到數(shù)字變?yōu)?。

Demo運(yùn)行效果

接下來(lái),我們看下不使用LiveData使用LiveData來(lái)實(shí)現(xiàn)這個(gè)Demo的區(qū)別

3.1、不使用LiveData

  • 代碼說(shuō)明如下:
    1、在MainActivity增加一個(gè)countDown的方法,通過(guò)CountDownTimer創(chuàng)建一個(gè)倒計(jì)時(shí)器
    2、new CountDownTimer(1 * 60 * 1000, 1 * 1000)中2個(gè)參數(shù)的含義:第一個(gè)參數(shù)表示這個(gè)Timer的總時(shí)長(zhǎng)為60秒;第2個(gè)參數(shù)表示每隔1秒中會(huì)更新一次,并回調(diào)onTick方法
    3、每次調(diào)用onTick方法,會(huì)調(diào)用textView.setText(String.valueOf(l / 1000));設(shè)置一次TextView的內(nèi)容

  • 對(duì)應(yīng)代碼如下:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "LiveDataDemo";

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);

        countDown();
    }

    private void countDown() {
        new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
            @Override
            public void onTick(long l) {
                Log.i(TAG, "onTick: " + l);
                textView.setText(String.valueOf(l / 1000));
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}
  • 弊端:
    業(yè)務(wù)邏輯(倒計(jì)時(shí)的功能)UI邏輯(TextView更新)沒(méi)有分開(kāi),耦合到一起了

3.2、使用MutableLiveData

為了方便演示,我們直接在MainActivity中操作LiveData,實(shí)際項(xiàng)目中不要這樣編寫(xiě)代碼,應(yīng)該把LiveData放到ViewModel中

Google推薦的使用LiveData的方法
  • 代碼說(shuō)明如下:
    1、在MainActivity中增加一個(gè)long類(lèi)型的MutableLiveData
    2、每次回調(diào)onTick方法時(shí),調(diào)用liveData.setValue(l);,把最新的值設(shè)置給LiveData
    3、調(diào)用LivewDataobserve方法,設(shè)置一個(gè)監(jiān)聽(tīng)器,每當(dāng)LiveData的值變化時(shí),會(huì)回調(diào)onChanged方法,在onChanged中設(shè)置TextView的內(nèi)容

  • 對(duì)應(yīng)代碼如下:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "LiveDataDemo";
    private final MutableLiveData<Long> liveData = new MutableLiveData<>();
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.textView);

        // TODO:為了方便演示,我們直接在MainActivity中操作LiveData,實(shí)際項(xiàng)目中不要這樣編寫(xiě)代碼,應(yīng)該把LiveData放到ViewModel中
        liveData.observe(this, new Observer<Long>() {
            @Override
            public void onChanged(Long aLong) {
                textView.setText(String.valueOf(aLong / 1000));
            }
        });

        countDown();
    }

    public void countDown() {
        new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
            @Override
            public void onTick(long l) {
                Log.i(TAG, "onTick: " + l);
                // TODO:為了方便演示,我們直接在MainActivity中操作LiveData,實(shí)際項(xiàng)目中不要這樣編寫(xiě)代碼,應(yīng)該把LiveData放到ViewModel中
                liveData.setValue(l);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}

3.3、使用MediatorLiveData

MediatorLiveData可以合并多個(gè)LiveData源,只要任何原始的LiveData源對(duì)象發(fā)生更改,就會(huì)觸發(fā)MediatorLiveData對(duì)象的觀察者。

例如,如果界面中有可以從本地?cái)?shù)據(jù)庫(kù)或網(wǎng)絡(luò)更新的 LiveData 對(duì)象,則可以向MediatorLiveData 對(duì)象添加以下源:

  • 與存儲(chǔ)在數(shù)據(jù)庫(kù)中的數(shù)據(jù)關(guān)聯(lián)的 LiveData 對(duì)象。
  • 與從網(wǎng)絡(luò)訪問(wèn)的數(shù)據(jù)關(guān)聯(lián)的 LiveData 對(duì)象。

Activity只需觀察MediatorLiveData對(duì)象即可從這兩個(gè)源接收更新。接下來(lái),我們會(huì)實(shí)現(xiàn)這個(gè)例子。

3.3.1、監(jiān)聽(tīng)2個(gè)數(shù)據(jù)源的變化

  • 代碼說(shuō)明如下:
    1、創(chuàng)建1個(gè)MediatorLiveData<String>對(duì)象

    final MediatorLiveData<String> liveDataMerger = new MediatorLiveData<>();
    

    2、創(chuàng)建2個(gè)MutableLiveData:liveData1表示網(wǎng)絡(luò)數(shù)據(jù)源,liveData2表示本地?cái)?shù)據(jù)源

    private MutableLiveData<String> liveData1 = new MutableLiveData<>();
    private MutableLiveData<String> liveData2 = new MutableLiveData<>();
    

    3、調(diào)用MediatorLiveDataaddSource方法,分別將liveData1liveData2加到MediatorLiveData要監(jiān)聽(tīng)的數(shù)據(jù)源中

    liveDataMerger.addSource(liveData1, new Observer<String>() {
      @Override
      public void onChanged(String s) {
          Log.i(TAG, "addSource1 onChanged: " + s);
          liveDataMerger.setValue(s);
      }
    });
    liveDataMerger.addSource(liveData2, new Observer<String>() {
      @Override
      public void onChanged(String s) {
          Log.i(TAG, "addSource2 onChanged: " + s);
          liveDataMerger.setValue(s);
      }
    });
    

    4、設(shè)置MediatorLiveData的監(jiān)聽(tīng),當(dāng)2個(gè)數(shù)據(jù)源發(fā)生變化時(shí),會(huì)回調(diào)onChanged方法,并將變化的內(nèi)容展示到TextView

    liveDataMerger.observe(this, new Observer<String>() {
      @Override
      public void onChanged(String s) {
          Log.i(TAG, "liveDataMerger onChanged: " + s);
          textView.setText(s);
      }
    

    });

3.3.2、 編寫(xiě)模擬2個(gè)數(shù)據(jù)源更新的代碼

編寫(xiě)一個(gè)mergeTes的方法,里面創(chuàng)建了2個(gè)Timer,用作模擬網(wǎng)絡(luò)數(shù)據(jù)更新和本地?cái)?shù)據(jù)更新的情況,每隔3秒會(huì)往網(wǎng)絡(luò)數(shù)據(jù)liveData1設(shè)置值、每隔10秒會(huì)往本地?cái)?shù)據(jù)liveData2設(shè)置值。對(duì)應(yīng)代碼如下:

public void mergeTest() {
    new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
        @Override
        public void onTick(long l) {
            liveData1.setValue("網(wǎng)絡(luò)有數(shù)據(jù)更新了" + l/1000);
        }

        @Override
        public void onFinish() {

        }
    }.start();

    new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
        @Override
        public void onTick(long l) {
            liveData2.setValue("本地?cái)?shù)據(jù)庫(kù)更新了" + l/1000);
        }

        @Override
        public void onFinish() {

        }
    }.start();
}

3.3.4、MediatorLiveData運(yùn)行效果

每隔3秒,TextView更新為如下內(nèi)容:

網(wǎng)絡(luò)數(shù)據(jù)有更新

每隔10秒,TextView更新為如下內(nèi)容:

本地?cái)?shù)據(jù)有更新

4、ViewModel使用

4.1 創(chuàng)建ViewModel類(lèi)

創(chuàng)建一個(gè)MyViewModel類(lèi),繼承自ViewModel

import androidx.lifecycle.ViewModel;

public class MyViewMode extends ViewModel  {
}

4.2 在Activity中使用

public class MainActivity extends AppCompatActivity {
    private MyViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
    }
}

如果遇到new ViewModelProvider(this)報(bào)錯(cuò),就在app/build.gradle中加入依賴implementation "androidx.lifecycle:lifecycle-viewmodel:2.3.0"

new ViewModelProvider(this)報(bào)錯(cuò)

5、結(jié)合ViewModel,重構(gòu)LiveData Demo

5.1、在ViewModel中增加LiveData

在MyViewModel中增加3個(gè)MutableLiveData,分別對(duì)應(yīng)在MainActivity中使用到的。并編寫(xiě)這3個(gè)LiveData的get方法

public class MyViewModel extends ViewModel {
    private MutableLiveData<Long> liveData = new MutableLiveData<>();
    private MutableLiveData<String> liveData1 = new MutableLiveData<>();
    private MutableLiveData<String> liveData2 = new MutableLiveData<>();

    public MutableLiveData<Long> getLiveData() {
        return liveData;
    }

    public MutableLiveData<String> getLiveData1() {
        return liveData1;
    }

    public MutableLiveData<String> getLiveData2() {
        return liveData2;
    }
}

5.2、在ViewModel中添加操作數(shù)據(jù)的邏輯

countDown、mergeTest這2個(gè)方法在操作數(shù)據(jù),從MainActivity中把這2個(gè)方法copy到 MyViewModel中

public class MyViewModel extends ViewModel {
    private static final String TAG = "MyViewMode";
    ... ...
    public void countDown() {
        new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
            @Override
            public void onTick(long l) {
                Log.i(TAG, "onTick: " + l);
                liveData.postValue(l);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }

    public void mergeTest() {
        new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
            @Override
            public void onTick(long l) {
                liveData1.postValue("網(wǎng)絡(luò)有數(shù)據(jù)更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();

        new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
            @Override
            public void onTick(long l) {
                liveData2.postValue("本地?cái)?shù)據(jù)庫(kù)更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}

5.3 修改MainActivity中和liveData有關(guān)的代碼

1、刪除countDown,改用viewModel.countDown();

    viewModel = new ViewModelProvider(this).get(MyViewModel.class);
    viewModel.countDown();

2、用viewModel.getLiveData().observe替換liveData.observe

    viewModel.getLiveData().observe(this, new Observer<Long>() {
        @Override
        public void onChanged(Long aLong) {
            textView.setText(String.valueOf(aLong/1000));
        }
    });

3、刪除liveData相關(guān)代碼

經(jīng)過(guò)上述修改后,liveData相關(guān)的重構(gòu)已完成,MainActivity代碼如下:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "LiveDataDemo";

    private TextView textView;

    private MutableLiveData<String> liveData1 = new MutableLiveData<>();
    private MutableLiveData<String> liveData2 = new MutableLiveData<>();
    private MyViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
        viewModel.countDown();

       .... ...
    }

5.4、修改MyViewModel

增加liveDataMerger字段,并編寫(xiě)getLiveDataMerger()方法,這個(gè)方法里面的內(nèi)容,是從MainActivity中copy過(guò)來(lái)的

public class MyViewModel extends ViewModel {
    private static final String TAG = "MyViewMode";
    .... ...
    private MediatorLiveData<String> liveDataMerger;

    public MediatorLiveData<String> getLiveDataMerger() {
        if (liveDataMerger == null) {
            liveDataMerger = new MediatorLiveData<>();
        }
        liveDataMerger.addSource(liveData1, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "addSource1 onChanged: " + s);
                liveDataMerger.setValue(s);
            }
        });
        liveDataMerger.addSource(liveData2, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "addSource2 onChanged: " + s);
                liveDataMerger.setValue(s);
            }
        });
        return liveDataMerger;
    }
   ... ...
}

5.5、修改MainActivity中和MediatorLiveData有關(guān)的代碼

1、使用viewModel.getLiveDataMerger().observe替換liveDataMerger.observe

    viewModel.getLiveDataMerger().observe(this, new Observer<String>() {
        @Override
        public void onChanged(String s) {
            Log.i(TAG, "liveDataMerger onChanged: " + s);
            textView.setText(s);
        }
    });

2、使用viewModel.mergeTest();替換mergeTest()

    viewModel.mergeTest();

3、刪除掉liveDataMerger相關(guān)的代碼

5.6 重構(gòu)后MainActivity的代碼

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "LiveDataDemo";

    private TextView textView;
    private MyViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView);

        viewModel = new ViewModelProvider(this).get(MyViewModel.class);

        viewModel.getLiveData().observe(this, new Observer<Long>() {
            @Override
            public void onChanged(Long aLong) {
                textView.setText(String.valueOf(aLong / 1000));
            }
        });

        viewModel.getLiveDataMerger().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "liveDataMerger onChanged: " + s);
                textView.setText(s);
            }
        });


        viewModel.mergeTest();
        // viewModel.countDown();
    }
}

5.7 重構(gòu)后MyViewModel的代碼

public class MyViewModel extends ViewModel {
    private static final String TAG = "MyViewMode";

    private MutableLiveData<Long> liveData = new MutableLiveData<>();
    private MutableLiveData<String> liveData1 = new MutableLiveData<>();
    private MutableLiveData<String> liveData2 = new MutableLiveData<>();
    private MediatorLiveData<String> liveDataMerger;

    public MediatorLiveData<String> getLiveDataMerger() {
        if (liveDataMerger == null) {
            liveDataMerger = new MediatorLiveData<>();
        }
        liveDataMerger.addSource(liveData1, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "addSource1 onChanged: " + s);
                liveDataMerger.setValue(s);
            }
        });
        liveDataMerger.addSource(liveData2, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.i(TAG, "addSource2 onChanged: " + s);
                liveDataMerger.setValue(s);
            }
        });
        return liveDataMerger;
    }

    public MutableLiveData<Long> getLiveData() {
        return liveData;
    }

    public MutableLiveData<String> getLiveData1() {
        return liveData1;
    }

    public MutableLiveData<String> getLiveData2() {
        return liveData2;
    }

    public void countDown() {
        new CountDownTimer(1 * 60 * 1000, 1 * 1000) {
            @Override
            public void onTick(long l) {
                Log.i(TAG, "onTick: " + l);
                liveData.postValue(l);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }

    public void mergeTest() {
        new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
            @Override
            public void onTick(long l) {
                liveData1.postValue("網(wǎng)絡(luò)有數(shù)據(jù)更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();

        new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
            @Override
            public void onTick(long l) {
                liveData2.postValue("本地?cái)?shù)據(jù)庫(kù)更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}

6、參考

LiveData 概覽
LiveData
MediatorLiveData
ViewModel 概覽

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容