Since its launch more than two years ago, LiveData has become an integral part of reactive programing. However, it is still not really perfect when we use The problem lies in observe function
1 2 3 4 5 6 7 8 | <span class="token keyword">val</span> someLiveData <span class="token operator">:</span> LiveData <span class="token operator"><</span> String <span class="token operator">></span> <span class="token operator">=</span> <span class="token operator">..</span> <span class="token punctuation">.</span> someLiveData <span class="token punctuation">.</span> <span class="token function">observe</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">,</span> Observer <span class="token punctuation">{</span> someString <span class="token operator">:</span> String <span class="token operator">?</span> <span class="token operator">-></span> <span class="token comment">// the value from the callback is always nullable</span> stringWhichIsNotNull <span class="token operator">=</span> someString <span class="token operator">?:</span> <span class="token string">"placeholder message"</span> <span class="token punctuation">}</span> |
However, with livedata-ktx version 2.2.0, we have an extension function
1 2 3 4 5 6 7 | <span class="token keyword">val</span> someLiveData <span class="token operator">:</span> LiveData <span class="token operator"><</span> String <span class="token operator">></span> <span class="token operator">=</span> <span class="token operator">..</span> <span class="token punctuation">.</span> viewModel <span class="token punctuation">.</span> someLiveData <span class="token punctuation">.</span> <span class="token function">observe</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> someString <span class="token operator">:</span> String <span class="token operator">-></span> <span class="token comment">// the value from this lambda is inferred as non-nullable</span> stringWhichI |
So we don't need to use it twice or we need unnecessary null checking. We can actually write code because it always handles nullability correctly.
But sometimes we don't get livedata from library. Sometimes we have to create them ourselves. While an observer function allows us to lose some templates at the receiving place, it does not solve all the problems.
1 2 3 4 5 6 | <span class="token keyword">val</span> someLiveData <span class="token operator">=</span> MutableLiveData <span class="token operator"><</span> String <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">val</span> currentValue <span class="token operator">=</span> someLiveData <span class="token punctuation">.</span> value <span class="token function">println</span> <span class="token punctuation">(</span> currentValue <span class="token punctuation">)</span> <span class="token comment">// null</span> |
In the above code we can see the problem. The data type is assumed to be String, but the value still returns the null value. You can easily understand why by looking at Java code
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">package</span> androidx <span class="token punctuation">.</span> lifecycle <span class="token punctuation">;</span> <span class="token annotation builtin">@Nullable</span> <span class="token keyword">public</span> T <span class="token function">getValue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> Object <span class="token keyword">data</span> <span class="token operator">=</span> mData <span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">data</span> <span class="token operator">!=</span> NOT_SET <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> T <span class="token punctuation">)</span> <span class="token keyword">data</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">null</span> <span class="token punctuation">;</span> |
But the more dangerous problem is that even though we defined in MutableLiveData <String> as non-nullable, we can still set its value to null.
1 2 3 4 5 | <span class="token keyword">val</span> someLiveData <span class="token operator">=</span> MutableLiveData <span class="token operator"><</span> String <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token comment">// this compiles!!</span> someLiveData <span class="token punctuation">.</span> value <span class="token operator">=</span> <span class="token keyword">null</span> |
Same as before, this is due to Java. It has no way to enforce nullable matches
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">package</span> androidx <span class="token punctuation">.</span> lifecycle <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">class</span> MutableLiveData <span class="token operator"><</span> T <span class="token operator">></span> extends LiveData <span class="token operator"><</span> T <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token operator">..</span> <span class="token punctuation">.</span> <span class="token annotation builtin">@Override</span> <span class="token keyword">public</span> void <span class="token function">setValue</span> <span class="token punctuation">(</span> T value <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> value <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
But we can fix them
Because LiveData is just a class, we can inherit them and add public set value methods so that it can be changed.
1 2 3 4 5 6 7 8 | <span class="token annotation builtin">@Suppress</span> <span class="token punctuation">(</span> <span class="token string">"UNCHECKED_CAST"</span> <span class="token punctuation">)</span> <span class="token keyword">class</span> MutableLiveData2 <span class="token operator"><</span> T <span class="token operator">></span> <span class="token punctuation">(</span> value <span class="token operator">:</span> T <span class="token punctuation">)</span> <span class="token operator">:</span> LiveData <span class="token operator"><</span> T <span class="token operator">></span> <span class="token punctuation">(</span> value <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">getValue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">:</span> T <span class="token operator">=</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">getValue</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">as</span> T <span class="token keyword">public</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> value <span class="token operator">:</span> T <span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> value <span class="token punctuation">)</span> <span class="token keyword">public</span> <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">postValue</span> <span class="token punctuation">(</span> value <span class="token operator">:</span> T <span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">super</span> <span class="token punctuation">.</span> <span class="token function">postValue</span> <span class="token punctuation">(</span> value <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
If you are using MutiableLiveData you can easily modify them with replace =)))
Previously you could assign a value using null values
And now we can not assign values with null as before anymore
If you find it interesting you can try using them
References
https://proandroiddev.com/improving-livedata-nullability-in-kotlin-45751a2bafb7