使用 LiveDataReactiveStreams 将 Flowable 转换为 LiveData 时出现 NullPointerException
NullPointerException on turning Flowable into LiveData using LiveDataReactiveStreams
我正在尝试将 Flowable 转换为 LiveData,但无法正常工作:
Flowable:(在存储库中)
fun searchMyObjectByName(query: String): Flowable<Array<MyObjectResponse>> {
return rest.searchMyObjectByName(query)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
LiveData:(在 ViewModel 中)
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.onChanged(SearchFragment.kt:46)
at com.xxx.xxx.ui.search.SearchFragment$onCreateView$resultObserver.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.onQueryTextChange(SearchFragment.kt:81)
at androidx.appcompat.widget.SearchView.onTextChanged(SearchView.java:1187)
at androidx.appcompat.widget.SearchView.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.this[=18=] : 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.onChanged(SearchFragment.kt:46)
at com.xxx.xxx.ui.search.SearchFragment$onCreateView$resultObserver.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)
好像调用了resultObserver的onChange,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.onChanged(ReactiveX.kt:19)
at com.example.playgroundapp.ReactiveX$nullValue$resultObserver.onChanged(ReactiveX.kt:9)
解决方法:
在调用 #replace.
之前可能会检查 null
为什么我认为这是竞争条件?
嗯,我没想到,NULL 是一个有效值。我虽然在访问 LiveData 时它应该总是 return 一个值(而不是 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
将始终为空。
@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;
}
如果不对 returned LiveData 进行适当的观察*,将没有任何价值。因此,您将 LiveData _myObject 设置为 null。
val resultObserver = Observer<ArrayList<MyObject>> { result -> adapter.replace(result) } //l.46
model.myObject.observe(viewLifecycleOwner, resultObserver)
使用 null
调用 resultObserver
并失败。
为什么这样做?
val resultObserver = Observer<ArrayList<MyObject>> {result -> adapter.replace(result)}
model.searchMyObjectByNameLiveData("query").observe(viewLifecycleOwner, resultObserver)
您正在通过 LiveData#observe* 订阅 Flowable。该值被接收并推送到 resultObserver
。因为该值不是 null
,所以您不会获得 NPE。
听起来不错?
我正在尝试将 Flowable 转换为 LiveData,但无法正常工作:
Flowable:(在存储库中)
fun searchMyObjectByName(query: String): Flowable<Array<MyObjectResponse>> {
return rest.searchMyObjectByName(query)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
LiveData:(在 ViewModel 中)
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.onChanged(SearchFragment.kt:46)
at com.xxx.xxx.ui.search.SearchFragment$onCreateView$resultObserver.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.onQueryTextChange(SearchFragment.kt:81)
at androidx.appcompat.widget.SearchView.onTextChanged(SearchView.java:1187)
at androidx.appcompat.widget.SearchView.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.this[=18=] : 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.onChanged(SearchFragment.kt:46)
at com.xxx.xxx.ui.search.SearchFragment$onCreateView$resultObserver.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)
好像调用了resultObserver的onChange,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.onChanged(ReactiveX.kt:19)
at com.example.playgroundapp.ReactiveX$nullValue$resultObserver.onChanged(ReactiveX.kt:9)
解决方法: 在调用 #replace.
之前可能会检查null
为什么我认为这是竞争条件?
嗯,我没想到,NULL 是一个有效值。我虽然在访问 LiveData 时它应该总是 return 一个值(而不是 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
将始终为空。
@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;
}
如果不对 returned LiveData 进行适当的观察*,将没有任何价值。因此,您将 LiveData _myObject 设置为 null。
val resultObserver = Observer<ArrayList<MyObject>> { result -> adapter.replace(result) } //l.46
model.myObject.observe(viewLifecycleOwner, resultObserver)
使用 null
调用 resultObserver
并失败。
为什么这样做?
val resultObserver = Observer<ArrayList<MyObject>> {result -> adapter.replace(result)}
model.searchMyObjectByNameLiveData("query").observe(viewLifecycleOwner, resultObserver)
您正在通过 LiveData#observe* 订阅 Flowable。该值被接收并推送到 resultObserver
。因为该值不是 null
,所以您不会获得 NPE。
听起来不错?