我试图将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。
听上去很好?