Using LiveData and ViewModel in Android

1 Overview

LiveDataIt is an observable data storage class, LiveDatausing the observer pattern, whenever the data changes, the objectLiveData will be notified , and we can update in these objectsObserverObserverUI

ViewModelObjects provide data for specific interface components (such as Fragment or Activity) and contain data processing business logic, which will be LiveDataused together

Next, we will first introduce how to use LiveData, and write a LiveData Demo, and then combine ViewModel to refactor the code of LiveData Demo

2. Instructions for using LiveData

LiveData<T>Is an abstract class, it has 2 subclasses: MutableLiveData<T>and MediatorLiveData<T>, when writing code, is the subclass created. Let's first look at MutableLiveData<T>the usage method, and then introduce how to use it in the following examplesMediatorLiveData<T>

2.1. Create a LiveData object

If the object class we want to observe is String, create an MutableLiveDataobject through the following code

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

2.2, observe the LiveData object

The data changes monitored by observethe method , whenever a change occurs, the method will be called back and the content of the value will be returnedLiveDataLiveDataonChangedString s

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

2.3. Update the LiveData object

LiveDataThe passed setValuemethod assigns a value, and the method LiveDatawill be called back after each value is assignedonChanged

liveData.setValue("add for test");

When assigning aLiveData value , the log will be output:"add for test"onChanged

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

3, editing LiveData Demo

We create a Demo with only one main interface to display a number. This number will be displayed starting from 59, and then the number will decrease by 1 every second until the number becomes 0 TextView.TextView

Demo running effect

Next, let's look at the difference between 不使用LiveDataand 使用LiveDatato implement this Demo

3.1. Do not use LiveData

  • The code description is as follows:
    1. In the method of MainActivityadding one countDown, by CountDownTimercreating a countdown timer
    2. new CountDownTimer(1 * 60 * 1000, 1 * 1000)The meaning of the two parameters: the first parameter indicates that the Timer 总时长is 60 seconds; the second parameter indicates that the timer 每隔1秒will be updated once and called back onTickMethod
    3. Every time onTickthe method is called, the content textView.setText(String.valueOf(l / 1000));set once will be calledTextView

  • The corresponding code is as follows:

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();
    }
}
  • Disadvantages:
    业务逻辑(倒计时的功能)and UI逻辑(TextView更新)not separated, coupled together

3.2, using MutableLiveData

为了方便演示,我们直接在MainActivity中操作LiveData,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中

Google's recommended way to use LiveData

  • The code description is as follows:
    1. MainActivityAdd a longtype in MutableLiveData
    2. onTickCall each time the method is called back, liveData.setValue(l);and set the latest value to LiveData
    3. Set a listener for the called method. Whenever LivewDatathe value of the method changes, the method will be called back . what is set inobserveLiveDataonChangedonChangedTextView

  • The corresponding code is as follows:

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,实际项目中不要这样编写代码,应该把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,实际项目中不要这样编写代码,应该把LiveData放到ViewModel中
                liveData.setValue(l);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}

3.3. Use MediatorLiveData

MediatorLiveDataMultiple sources can be merged and the object's observer will be triggered LiveDatawhenever any of the original LiveDatasource objects changes .MediatorLiveData

For example, if you have LiveDataan object , you can MediatorLiveDataadd the following source to the object:

  • LiveDataObjects associated with data stored in the database .
  • LiveDataAn object associated with data accessed from the network .

ActivitySimply observe MediatorLiveDatathe object to receive updates from both sources. Next, we will implement this example.

3.3.1. Monitor the changes of two data sources

  • The code description is as follows:
    1. Create an MediatorLiveData<String>object

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

    2. Create two MutableLiveData: liveData1 represents the network data source, liveData2 represents the local data source

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

    3. The method to call MediatorLiveData, addSourcerespectively add liveData1and to the data source to be monitoredliveData2MediatorLiveData

    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. Set MediatorLiveDatathe monitoring, when the two data sources change, onChangedthe method will be called back, and the changed content will be displayed on TextViewthe

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

    });

3.3.2. Write the code that simulates the update of two data sources

Write a mergeTesmethod, which is created 2个Timerto simulate the situation of network data update and local data update. The value 3will be set to the network data every second liveData1, and the value 10will be set to the local data every second liveData2. The corresponding code is as follows:

public void mergeTest() {
    new CountDownTimer(1 * 60 * 1000, 3 * 1000) {
        @Override
        public void onTick(long l) {
            liveData1.setValue("网络有数据更新了" + l/1000);
        }

        @Override
        public void onFinish() {

        }
    }.start();

    new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
        @Override
        public void onTick(long l) {
            liveData2.setValue("本地数据库更新了" + l/1000);
        }

        @Override
        public void onFinish() {

        }
    }.start();
}

3.3.4, MediatorLiveData running effect

Every 3 seconds, TextViewthe update is as follows:

Network data has been updated

Every 10 seconds, TextViewthe update is as follows:

Local data has been updated

4. Use of ViewModel

4.1 Create ViewModel class

Create a MyViewModel class that inherits from ViewModel

import androidx.lifecycle.ViewModel;

public class MyViewMode extends ViewModel  {
}

4.2 Use in 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);
    }
}

If you encounter new ViewModelProvider(this)an error, add dependencies to app/build.gradleimplementation "androidx.lifecycle:lifecycle-viewmodel:2.3.0"

5. Combining with ViewModel, refactor LiveData Demo

5.1. Add LiveData to ViewModel

Add 3 MutableLiveData in MyViewModel, corresponding to those used in MainActivity. And write the get method of these 3 LiveData

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. Add logic for manipulating data in ViewModel

The two methods of countDown and mergeTest are operating data, copy these two methods from MainActivity to 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("网络有数据更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();

        new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
            @Override
            public void onTick(long l) {
                liveData2.postValue("本地数据库更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}

5.3 Modify the code related to liveData in MainActivity

1. Delete countDown and use viewModel.countDown() instead;

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

2. Replace liveData.observe with viewModel.getLiveData().observe

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

3. Delete liveData related code

After the above modifications, the refactoring related to liveData has been completed, and the code of MainActivity is as follows:

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. Modify MyViewModel

Add the liveDataMerger field and write the getLiveDataMerger() method. The content in this method is copied from MainActivity

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. Modify the code related to MediatorLiveData in MainActivity

1. Use viewModel.getLiveDataMerger().observe to replace 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. Use viewModel.mergeTest(); replace mergeTest()

    viewModel.mergeTest();

3. Delete the code related to liveDataMerger

5.6 Code of MainActivity after refactoring

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 Code of MyViewModel after refactoring

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("网络有数据更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();

        new CountDownTimer(1 * 60 * 1000, 10 * 1000) {
            @Override
            public void onTick(long l) {
                liveData2.postValue("本地数据库更新了" + l / 1000);
            }

            @Override
            public void onFinish() {

            }
        }.start();
    }
}

6, reference

LiveData OverviewLiveData MediatorLiveData
ViewModel Overview

Guess you like

Origin blog.csdn.net/xige1995/article/details/127362710