With the help of the ability of LiveData, LiveData communication is used between View (Activity or Fragment) and ViewModel. When LiveData changes, the View subscribed to the LiveData can receive a notification to update UI logic accordingly.
This feature is very useful and convenient for scenarios that need to continuously monitor data changes and then make UI response in real time.

However, there is a detail to note here: the View can receive a notification and get the current LiveData value at the moment of observing LiveData. In other words, when listening to LiveData, we can get the value set to LiveData before listening - we call it sticky message.
But sometimes, we don't want to get the "last value" when listening. For example, we implement the function of collection receipt reminder. We hope to send a reminder of receipt notice every time we receive a new account. Assuming that there is a receipt record before we listen, if I start listening and remind me of a new receipt (in fact, the last receipt), there is a problem.
This feature of LiveData is not a Bug. At the beginning of its design, LiveData is not used as an EventBus, but is used to listen to "status". It is referenced here Guan Bo Original text
Instead of trying to solve this with libraries or extensions to the Architecture Components, it should be faced as a design problem.
How to understand state and event?
Simply understand: "state" can last for a period of time, and "event" refers to what happens at a certain moment
Take a simple example: turn on and off the lights

- Turning off the light and turning on the light can be understood as two events, while the light is on and the light is off are two states;
- After the light is on, you can get the status of the light (the status is continuous), but you can't know when the light was turned on (the event occurred before and passed quickly)
- The triggering of the event can make the state transition
The LiveData feature "can receive the changed state before listening" is designed for "state". Therefore, not all scenarios are suitable for using LiveData. When the data we want to monitor conforms to the "state" feature, but not the "event" feature, it is the most suitable scenario for using LiveData.
Several common incorrect postures of LiveData
The reason why we spent so much time introducing the "sticky message" feature of LiveData is that first, we can have a deeper understanding of LiveData and understand its purpose at the beginning of design, so as to know when to use it and when not to use it. Second, if we can't understand this feature, it is easy to cause problems. The following describes several common misuses:
case 1: the data is of type "event"
Take "collection receipt reminder as an example"
class MvvmViewModel : ViewModel() { private val _billLiveData = MutableLiveData<String>() val billLiveData: LiveData<String> get() = _billLiveData fun pay(msg: String) { _billLiveData.value = msg } } Copy code
class MvvmActivity : AppCompatActivity() { private val viewModel by viewModels<MvvmViewModel>() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_mvvm) viewModel.pay("Xiao Ming paid 100 yuan") btn.setOnClickListener { viewModel.pay("Xiao Zhang paid 800 yuan") } viewModel.billLiveData.observe(this, Observer { Log.d("sample", it) }) } } Copy code
After running, no operation will be done, and the following log will be entered:
sample: Xiao Ming paid 100 yuan Copy code
In fact, it doesn't meet the expectations here. Xiao Ming paid 100 yuan before, and I started listening after that. At the moment, I don't need to inform me of what happened before
In this case, it is not recommended to use LiveData, although various workaround methods are used (refer to my other article here: Exploration and attempt of LiveData non sticky message )It may meet the requirements, but LiveData has its own specific use scenarios. If you have to break through the restrictions to use it, it will make LiveData more difficult to understand
Another article is quoted here Blog Original text
The Anti-Pattern Using LiveData objects for events can be taken as a bad design decision, even more when that event needs to be consumed just once. We can have a workaround using some boolean flags to help the view decide whether the Dialog/SnackBar must be triggered/shown or not. But this can lead to a solution difficult to read/maintain and ugly as well.
case 2: there are multiple listeners and the LiveData is modified in one observer
Do not modify the value of LiveData in the observer, which will affect other observers. For example, the following code:
class MvvmViewModel : ViewModel() { private val _liveData = MutableLiveData<User>() val liveData: LiveData<User> get() = _liveData fun setNewValue(user: User) { _liveData.value = user } } data class User(var name: String) Copy code
class MvvmActivity : AppCompatActivity() { private val viewModel by viewModels<MvvmViewModel>() private val observer1 = object : Observer<User> { override fun onChanged(t: User?) { Log.d("sample", "observer1 onChanged: $t") t?.name = "Michael" // The name of user in LiveData is modified here } } private val observer2 = object : Observer<User> { override fun onChanged(t: User?) { Log.d("sample", "observer2 onChanged: $t") } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_mvvm) btn.setOnClickListener { viewModel.setNewValue(User("Joe")) } viewModel.liveData.observe(this, observer1) viewModel.liveData.observe(this, observer2) } } Copy code
- We modified the value of name in User in observer1
- At this time, if we click the button and send a User("Joe"), observer1 and observer2 will output the following logs respectively
observer1 onChanged: User(name=Joe) observer2 onChanged: User(name=Michael) Copy code
Therefore, unless we have such requirements, do not change the value referenced by LiveData in observer.
A better design should define T in LiveData as Immutable. For example, the User in the example can be defined as follows
data class User(val name: String) // name is immutable Copy code
This can effectively prevent the problem of modifying LiveData data data in observer
case 3: adding an observer repeatedly
class MvvmActivity : AppCompatActivity() { private val viewModel by viewModels<MvvmViewModel>() private var count = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_mvvm) btn.setOnClickListener( View.OnClickListener { viewModel.pay("100 element") }) btn_wait.setOnClickListener { viewModel.billLiveData.observe(this, Observer { Log.d("sample", "I got it ${count++}") }) } } } Copy code
The above code creates a new observer instance every time it listens. If you click btn at this time, you will receive multiple listens (because multiple observers are registered)
sample: Received 0 sample: Received 1 sample: Received 2 sample: Received 3 Copy code
Therefore, unless required by a special scenario, use it carefully and create a new instance every time
case 4: incorrect use of lifecycle owner
A very common scenario: listen to the LiveData of ViewModel in the ViewHolder of RecycleView, and then the passed Lifecycle owner is the corresponding Fragment. Since the life cycle of the ViewHolder is shorter than that of the Fragment, when the ViewHolder is destroyed, because the life cycle of the Fragment has not ended, the ViewHolder will have a memory leak (the monitored LiveData is not unbound)
There are two solutions in this scenario:
- Use the observaveforever of LiveData, and then manually call removeObserver before the ViewHolder is destroyed
- Use lifecycle registry to distribute lifecycle to ViewHolder. See another article: Customize life cycle and realize life cycle awareness
Related articles
Exploration and attempt of LiveData non sticky message
Using Architecture Component to realize the correct posture of MVVM