Change theme
Help
Press space for more information.
Show links for this issue (Shortcut: i, l)
Copy issue ID
Previous Issue (Shortcut: k)
Next Issue (Shortcut: j)
Sign in to use full features.
Vote: I am impacted
Notification menu
Refresh (Shortcut: Shift+r)
Go home (Shortcut: u)
Pending code changes (auto-populated)
View issue level access limits(Press Alt + Right arrow for more information)
Attachment actions
Unintended behavior
View staffing
Description
Version used: 2.1.0-alpha01
Devices/Android versions reproduced on: API 28 emulator, Chrome OS
MediatorLiveData.addSource doesn't manually check for null source argument (annotation is there, which is good)
It's possible to accidentally add a null (or not yet initialized) source.
The issue is buried until the MediatorLiveData is observed, making it hard to find the original issue.
Best solution would be a quick check of the variables and fast fail before processing. e.g. assertNotNull(source)
- Sample project attached
relevant code:
private val mediatorLiveData = MediatorLiveData<Int>().apply {
// Here is the mistake, (allowed at compile time).
addSource(dataSource) { value = (it ?: 0) + 1 }
}
private val dataSource = MutableLiveData<Int>()
// This triggers the exception
mediatorLiveData.observe(this, Observer { // process })
Error output without check: (unhelpful unless you know the internals of MediatorLiveData)
2019-01-18 10:58:39.891 6385-6385/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.google.example.mediatorlatecheck, PID: 6385
java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.lifecycle.LiveData.observeForever(androidx.lifecycle.Observer)' on a null object reference
at androidx.lifecycle.MediatorLiveData$Source.plug(MediatorLiveData.java:141)
at androidx.lifecycle.MediatorLiveData.onActive(MediatorLiveData.java:118)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:436)
at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged(LiveData.java:394)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:355)
at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:293)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:333)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:138)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:124)
at androidx.lifecycle.ReportFragment.dispatch(ReportFragment.java:123)
at androidx.lifecycle.ReportFragment.onStart(ReportFragment.java:83)
at android.app.Fragment.performStart(Fragment.java:2548)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1334)
at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1576)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1637)
at android.app.FragmentManagerImpl.dispatchMoveToState(FragmentManager.java:3046)
at android.app.FragmentManagerImpl.dispatchStart(FragmentManager.java:3003)
at android.app.FragmentController.dispatchStart(FragmentController.java:193)
at android.app.Activity.performStart(Activity.java:7165)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:2937)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)