MVVM // 在 Activity 旋转时触发 ViewModel 事件(重新创建)
MVVM // ViewModel event being fired on Activity rotation (recreated)
阅读 Google docs 我发现(有点)使用 selectedItem
的示例,以便将被触发的事件传播给其他观察者,这是我当前的实现:
ViewModel
public void onListItemClicked(Item item) {
if (selectedItem.getValue() == item) {
return;
}
selectedItem.postValue(item);
}
public LiveData<Item> getSelectedItem() {
if (selectedItem == null) {
selectedItem = new MutableLiveData<>();
}
return selectedItem;
}
查看
ListViewModel viewModel = ViewModelProviders.of(this).get(ListViewModel.class);
viewModel.getSelectedItem().observe(this, new Observer<Item>() {
@Override
public void onChanged(@Nullable Item item) {
if (item != null) {
openDetailActivity(item);
}
}
});
并且当用户 单击列表时:
@Override
public void onItemClicked(Item item) {
viewModel.onListItemClicked(item);
}
所有一切正常,问题是当用户旋转屏幕并且ListActivity
重新-created 检测到更改并将在订阅时打开 DetailActivity
。
我找到了一种解决方法,即在 getSelectedItem()
上添加 selectedItem.postValue(null);
,但它有点笨拙。
Ofc 可能会争辩说打开细节 activity 和传播偶数应该分开,但我想知道是否有人有更好的 implementation/suggestion。
编辑
使用 SingleLiveEvent
是正确的方法。这可确保您的 ViewModel
仅触发一次事件。
参考文章如下:
我用 Kotlin
class 创建了一个要点。
我一直在成功使用这些用例:
我会保持要点是最新的,但我也会在这里留下代码
(仅供参考,这可能已过时,因为我不会在每次更改要点时都编辑此答案):
package YOUR_PACKAGE
import androidx.annotation.MainThread
import androidx.annotation.Nullable
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
* <p>
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
* <p>
* Note that only one observer is going to be notified of changes.
*/
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
// Observe the internal MutableLiveData
super.observe(owner, Observer<T> { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
@MainThread
override fun setValue(@Nullable t: T?) {
mPending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
}
旧答案:
所以在进行了大量研究并与 Google 开发人员取得联系之后。推荐的解决方案是有单独的职责。
打开一个 activity 应该是对点击事件的响应而不是实际的变化,这种类型的 selectedItem
场景对于 与其他监听视图的解耦通信 特别有用。
例如同一activity
中的另一个片段
阅读 Google docs 我发现(有点)使用 selectedItem
的示例,以便将被触发的事件传播给其他观察者,这是我当前的实现:
ViewModel
public void onListItemClicked(Item item) {
if (selectedItem.getValue() == item) {
return;
}
selectedItem.postValue(item);
}
public LiveData<Item> getSelectedItem() {
if (selectedItem == null) {
selectedItem = new MutableLiveData<>();
}
return selectedItem;
}
查看
ListViewModel viewModel = ViewModelProviders.of(this).get(ListViewModel.class);
viewModel.getSelectedItem().observe(this, new Observer<Item>() {
@Override
public void onChanged(@Nullable Item item) {
if (item != null) {
openDetailActivity(item);
}
}
});
并且当用户 单击列表时:
@Override
public void onItemClicked(Item item) {
viewModel.onListItemClicked(item);
}
所有一切正常,问题是当用户旋转屏幕并且ListActivity
重新-created 检测到更改并将在订阅时打开 DetailActivity
。
我找到了一种解决方法,即在 getSelectedItem()
上添加 selectedItem.postValue(null);
,但它有点笨拙。
Ofc 可能会争辩说打开细节 activity 和传播偶数应该分开,但我想知道是否有人有更好的 implementation/suggestion。
编辑
使用 SingleLiveEvent
是正确的方法。这可确保您的 ViewModel
仅触发一次事件。
参考文章如下:
我用 Kotlin
class 创建了一个要点。
我一直在成功使用这些用例:
我会保持要点是最新的,但我也会在这里留下代码 (仅供参考,这可能已过时,因为我不会在每次更改要点时都编辑此答案):
package YOUR_PACKAGE
import androidx.annotation.MainThread
import androidx.annotation.Nullable
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
* <p>
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
* <p>
* Note that only one observer is going to be notified of changes.
*/
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
// Observe the internal MutableLiveData
super.observe(owner, Observer<T> { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
@MainThread
override fun setValue(@Nullable t: T?) {
mPending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
}
旧答案:
所以在进行了大量研究并与 Google 开发人员取得联系之后。推荐的解决方案是有单独的职责。
打开一个 activity 应该是对点击事件的响应而不是实际的变化,这种类型的 selectedItem
场景对于 与其他监听视图的解耦通信 特别有用。
例如同一activity