mht*_*ani 5 android viewmodel kotlin android-livedata
我最近看到了一个奇怪的问题,它正在阻碍我的项目。多次调用以设置实时数据值不会调用视图中的观察者。
似乎只有最后设置的值才实际调用视图中的Observer。
这是评论的代码段。
class MainActivity : AppCompatActivity() {
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this).get(MainViewModelImpl::class.java)
viewModel.state().observe(this, Observer {
onStateChange(it!!)
})
viewModel.fetchFirstThree()
}
private fun onStateChange(state: MainViewModel.State) {
when (state) {
is One -> {
show(state.data)
}
is Two -> {
show(state.data)
}
is Three -> {
show(state.data)
}
}
}
private fun show(data: String) {
Log.d("Response", data)
}
}
Run Code Online (Sandbox Code Playgroud)
abstract class MainViewModel : ViewModel() {
sealed class State {
data class One(val data: String) : State()
data class Two(val data: String) : State()
data class Three(val data: String) : State()
}
abstract fun state(): LiveData<State>
abstract fun fetchFirstThree()
}
Run Code Online (Sandbox Code Playgroud)
class MainViewModelImpl : MainViewModel() {
private val stateLiveData: MediatorLiveData<State> = MediatorLiveData()
override fun state(): LiveData<State> = stateLiveData
override fun fetchFirstThree() {
stateLiveData.value = State.One("One")
stateLiveData.value = State.Two("Two")
stateLiveData.value = State.Three("Three")
}
}
Run Code Online (Sandbox Code Playgroud)
预期产量:
Response: One
Response: Two
Response: Three
Run Code Online (Sandbox Code Playgroud)
实际输出:
Response: Three
Run Code Online (Sandbox Code Playgroud)
根据上面的输出,前两个值未调用Observer。
我做了一些科学,重新实现 LiveData 和 MutableLiveData 来注销一些数据。
setValue value=Test1
dispatchingValue mDispatchingValue=false mDispatchInvalidated=false
considerNotify
Returned at !observer.active
setValue value=Test2
dispatchingValue mDispatchingValue=false mDispatchInvalidated=false
considerNotify
Returned at !observer.active
setValue value=Test3
dispatchingValue mDispatchingValue=false mDispatchInvalidated=false
considerNotify
Returned at !observer.active
dispatchingValue mDispatchingValue=false mDispatchInvalidated=false
considerNotify
ITEM: Test3
Run Code Online (Sandbox Code Playgroud)
当您发送初始值时,观察者似乎尚未达到活动状态。
private void considerNotify(LifecycleBoundObserver observer) {
// <-- Three times it fails here. This means that your observer wasn't ready for any of them.
if (!observer.active) {
return;
}
Run Code Online (Sandbox Code Playgroud)
一旦观察者达到活动状态,它就会发送最后一个设置值。
void activeStateChanged(boolean newActive) {
if (newActive == active) {
return;
}
active = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += active ? 1 : -1;
if (wasInactive && active) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !active) {
onInactive();
}
if (active) {
// <--- At this point you are getting a call to your observer!
dispatchingValue(this);
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
您可以像这样使用自定义 LiveData:
class ActiveMutableLiveData<T> : MutableLiveData<T>() {
private val values: Queue<T> = LinkedList()
private var isActive: Boolean = false
override fun onActive() {
isActive = true
while (values.isNotEmpty()) {
setValue(values.poll())
}
}
override fun onInactive() {
isActive = false
}
override fun setValue(value: T) {
if (isActive) {
super.setValue(value)
} else {
values.add(value)
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我也有这样的问题。
为了解决这个问题,我们创建了自定义 MutableLiveData,它包含一个发布值队列,并将为每个值通知观察者。
您可以像通常的 MutableLiveData 一样使用它。
open class MultipleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
private val values: Queue<T> = LinkedList()
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(this::class.java.name, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, { t: T ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
//call next value processing if have such
if (values.isNotEmpty())
pollValue()
}
})
}
override fun postValue(value: T) {
values.add(value)
pollValue()
}
private fun pollValue() {
value = values.poll()
}
@MainThread
override fun setValue(t: T?) {
mPending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@Suppress("unused")
@MainThread
fun call() {
value = null
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1825 次 |
| 最近记录: |