Search code examples
androidkotlinandroid-jetpack

How do I initialize a collection value for a MutableLiveData object?


class Foo : ViewModel() {
   val bars: MutableLiveData<ArrayList<Bar>> = MutableLiveData()
     get() {
       if(field.value == null) {
         field.setValue(ArrayList()) // NullPointerException
       }
     }
}

class FooTest(){
   @Test fun itShouldNotBlowUp() {
     Foo() //nullPointerException

   }

}

I don't understand how to initialize the value of a MutableLiveData object. I've tried to lazily initialize it via the getter and with an init block. Both approaches throw a null pointer when setting the value. bars is not null however.

Here is the stacktrace:

java.lang.NullPointerException
at android.arch.core.executor.DefaultTaskExecutor.isMainThread(DefaultTaskExecutor.java:58)
at android.arch.core.executor.ArchTaskExecutor.isMainThread(ArchTaskExecutor.java:116)
at android.arch.lifecycle.LiveData.assertMainThread(LiveData.java:434)
at android.arch.lifecycle.LiveData.setValue(LiveData.java:279)
at android.arch.lifecycle.MutableLiveData.setValue(MutableLiveData.java:33)
at org.Foo.<init>(Foo.kt:10)
at org.FooTest.ShouldNotBlowUp(FooTest.kt:3)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

How do I initialize the ArrayList and set it as the value of bars?


Solution

  • Maybe the reason is that you are trying to run this in the test. Your sample fails in DefaultTaskExecutor.isMainThread(), that looks like this:

    public boolean isMainThread() {
        return Looper.getMainLooper().getThread() == Thread.currentThread();
    }
    

    So Looper.getMainLooper() returns null in the test environment.

    And also, have you tried to initialize property via 'lazy' delegate?

    val bars: MutableLiveData<List<Bar>> by lazy {
        MutableLiveData<List<Bar>>().apply {
            value = emptyList()
        }
    }
    

    That works fine in my case and feels more idiomatic