NullPointerException on 将 Flowable 转换为 LiveData 使用 LiveDataR



我试图将Flowable变成LiveData,但我无法使其工作:

可流动:(在存储库中)

fun searchMyObjectByName(query: String): Flowable<Array<MyObjectResponse>> {
return rest.searchMyObjectByName(query)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}

实时数据:(在视图中模型)

private val _myObject = MutableLiveData<ArrayList<MyObject>>()
val myObject: LiveData<ArrayList<MyObject>>
get() = _myObject
fun searchMyObjectByNameLiveData(query: String) {
_myObject.value = LiveDataReactiveStreams.fromPublisher(repo.searchMyObjectByName(query).map { it -> responseToObject(it) }).value
}

观察者:(Fragment@OnCreateView)

// should be empty at first and then restore the value ...
val resultObserver = Observer<ArrayList<MyObject>> { result -> adapter.replace(result) } //l.46
model.myObject.observe(viewLifecycleOwner, resultObserver)

搜索由用户触发:

model.searchMyObjectByNameLiveData(query)

我得到的NPE错误:

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.xxx.xxx, PID: 13700
java.lang.NullPointerException: result must not be null
at com.xxx.xxx.ui.search.SearchFragment$onCreateView$resultObserver$1.onChanged(SearchFragment.kt:46)
at com.xxx.xxx.ui.search.SearchFragment$onCreateView$resultObserver$1.onChanged(SearchFragment.kt:26)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at com.xxx.xxx.ui.search.SearchViewModel.searchMedicByNameTest2(SearchViewModel.kt:56)
at com.xxx.xxx.ui.search.SearchFragment.startSearching(SearchFragment.kt:137)
at com.xxx.xxx.ui.search.SearchFragment$onCreateOptionsMenu$2.onQueryTextChange(SearchFragment.kt:81)
at androidx.appcompat.widget.SearchView.onTextChanged(SearchView.java:1187)
at androidx.appcompat.widget.SearchView$10.onTextChanged(SearchView.java:1725)
at android.widget.TextView.sendOnTextChanged(TextView.java:10631)
at android.widget.TextView.handleTextChanged(TextView.java:10721)
at android.widget.TextView$ChangeWatcher.onTextChanged(TextView.java:13477)
at android.text.SpannableStringBuilder.sendTextChanged(SpannableStringBuilder.java:1267)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:576)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:507)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:37)
at android.view.inputmethod.BaseInputConnection.replaceText(BaseInputConnection.java:869)
at android.view.inputmethod.BaseInputConnection.setComposingText(BaseInputConnection.java:636)

如果我直接观察该方法,它正在工作:

val resultObserver = Observer<ArrayList<MyObject>> {result -> adapter.replace(result)} 
model.searchMyObjectByNameLiveData("query").observe(viewLifecycleOwner, resultObserver)

但这不是我想要的,因为此时我没有用户的输入。

感谢您的帮助。

编辑:

片段中的侦听器

override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.options_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
Timber.i("onCreateOptionsMenu")
searchView = SearchView((context as MainActivity).supportActionBar!!.themedContext)
menu.findItem(R.id.search).apply {
setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW or MenuItem.SHOW_AS_ACTION_IF_ROOM)
actionView = searchView
}
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(query: String): Boolean {
when (newText.length) {
in 0..2 -> adapter.clear();
else -> {
model.searchMyObjectByNameLiveData(query)
}
}
return false
}
})
}

编辑#2:

LINENUMBER 47 L0
ALOAD 0
GETFIELD com/xxx/xxx/ui/search/SearchFragment$onCreateView$1.this$0 : Lcom/xxx/xxx/ui/search/SearchFragment;
INVOKEVIRTUAL com/xxx/xxx/ui/search/SearchFragment.getAdapter ()Lcom/xxx/xxx/adapter/AdapterMedicSearch;
ALOAD 1
DUP
LDC "result"
INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkNotNullExpressionValue (Ljava/lang/Object;Ljava/lang/String;)V
INVOKEVIRTUAL com/xxx/xxx/adapter/AdapterMedicSearch.replace (Ljava/util/ArrayList;)V
L1

我会说,问题如下:

val resultObserver = Observer<ArrayList<MyObject>> { result -> adapter.replace(result) } //l.46

看来结果是null.

只需按照堆栈跟踪从按钮到顶部

java.lang.NullPointerException: result must not be null
at com.xxx.xxx.ui.search.SearchFragment$onCreateView$resultObserver$1.onChanged(SearchFragment.kt:46)
at com.xxx.xxx.ui.search.SearchFragment$onCreateView$resultObserver$1.onChanged(SearchFragment.kt:26)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at com.xxx.xxx.ui.search.SearchViewModel.searchMedicByNameTest2(SearchViewModel.kt:56)

似乎调用了onChange 的 resultObserver ,并且 lambda 抛出了一个 NPE,因为result数据是null的。

如果添加if != null check?会发生什么情况。

总而言之,我会说这是一个竞争条件,需要适当的同步,但是如果没有完整的代码,就很难说为什么第一个值是null的。提示它是争用条件可能是,在调试它时,由于时间更改,它将起作用。

更新:NPE

我无法重现您的问题,但是当与Java交互时,可能会发生NPE

class ReactiveX {
@Test
fun nullValue() {
val mutableLiveData = MutableLiveData<List<MyObject>>()
Handler(Looper.getMainLooper()).post {
mutableLiveData.value = null
}
val resultObserver = Observer<List<MyObject>> { result: List<MyObject>? ->
val returnedVal: List<MyObject> = Adapter.replace(result)
}
mutableLiveData.observeForever(resultObserver)
}
}
internal data class MyObject(private val s: String)
public class Adapter {
static List<MyObject> replace(List<MyObject> result) {
return result;
}
}

NPE

java.lang.NullPointerException: Adapter.replace(result) must not be null
at com.example.playgroundapp.ReactiveX$nullValue$resultObserver$1.onChanged(ReactiveX.kt:19)
at com.example.playgroundapp.ReactiveX$nullValue$resultObserver$1.onChanged(ReactiveX.kt:9)

溶液: 在拨打 #replace 之前,可能会检查null

为什么我认为这是一种竞争条件?

好吧,我没想到,NULL 是一个有效的值。我虽然在访问 LiveData 时它应该始终返回一个值(而不是null)

@Test
fun fromCallableNotCalledWithoutObserve() {
val subscribeActualCalled = AtomicBoolean(false)
val sync = Flowable.fromCallable {
subscribeActualCalled.compareAndSet(false, true)
42
}
val fromPublisher = LiveDataReactiveStreams.fromPublisher(sync)
val value = fromPublisher.value
assertThat(value).isNull()
assertThat(subscribeActualCalled.get()).isFalse()
}

fromCallableNotCalledWithoutObserve显示,如果不调用任何订阅方法,则该value将始终为 null。

@Test
fun valueStillNull() {
val initValue = AtomicInteger(-1)
val subscribeActualCalled = AtomicBoolean(false)
val flowable = Flowable.fromCallable {
subscribeActualCalled.compareAndSet(false, true)
Thread.sleep(1000)
42
}.subscribeOn(Schedulers.io())
val fromPublisher = LiveDataReactiveStreams.fromPublisher(flowable)
val value = fromPublisher.value
fromPublisher.observeForever {
initValue.compareAndSet(-1, it ?: 666)
}
assertThat(value).isNull()
assertThat(subscribeActualCalled.get()).isTrue()
assertThat(initValue.get()).isEqualTo(-1)
}

valueStillNull显示,在没有正确的 #observe* 方法的情况下访问 LiveData 的值将导致默认值,在本例中为null

这段代码有什么作用?

fun searchMyObjectByNameLiveData(query: String) {
_myObject.value = LiveDataReactiveStreams.fromPublisher(repo.searchMyObjectByName(query).map { it -> responseToObject(it) }).value
}

它实际上设置了 _myObject.value = null,因为

@Nullable
public T getValue() {
Object data = mData;
if (data != NOT_SET) {
//noinspection unchecked
return (T) data;
}
return null;
}

如果没有对返回的实时数据进行适当的观察*,则不会有任何值。因此,您将实时数据_myObject设置为 null。

val resultObserver = Observer<ArrayList<MyObject>> { result -> adapter.replace(result) } //l.46
model.myObject.observe(viewLifecycleOwner, resultObserver)

resultObserver被调用null并失败。

为什么会这样?

val resultObserver = Observer<ArrayList<MyObject>> {result -> adapter.replace(result)} 
model.searchMyObjectByNameLiveData("query").observe(viewLifecycleOwner, resultObserver)

您正在通过 LiveData#observe* 订阅 Flowable。接收值并将其推送到resultObserver。由于该值不null因此不会获得 NPE。

听上去很好?

最新更新