不知道谷歌是否对嵌套 ViewModel 有很好的支持,看起来没有。
值得庆幸的是,我们不需要坚持androidx.lifecycle.ViewModel 在我们需要的地方应用 MVVM 方法。我决定写一个小例子:
片段,没有任何变化:
@Override public void onCreate(@Nullable Bundle savedInstanceState) {
final ItemListAdapter adapter = new ItemListAdapter();
binding.getRoot().setAdapter(adapter);
viewModel = new ViewModelProvider(this).get(ItemListViewModel.class);
viewModel.getItems().observe(getViewLifecycleOwner(), adapter::submitList);
}
ItemListAdapter,除了填充视图之外,它还负责通知项目的观察者——他们是否继续收听。在我的示例适配器中,ListAdapter 扩展了 RecyclerView.Adapter,因此它接收项目列表。这是无意的,只是编辑了一些我已经拥有的代码。使用不同的基本实现可能要好得多,但出于演示目的是可以接受的:
@Override public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
return new Holder(parent);
}
@Override public void onBindViewHolder(Holder holder, int position) {
holder.lifecycle.setCurrentState(Lifecycle.State.RESUMED);
holder.bind(getItem(position));
}
@Override public void onViewRecycled(Holder holder) {
holder.lifecycle.setCurrentState(Lifecycle.State.DESTROYED);
}
// Idk, but these both may be used to pause/resume, while bind/recycle for start/stop.
@Override public void onViewAttachedToWindow(Holder holder) { }
@Override public void onViewDetachedFromWindow(Holder holder) { }
Holder. 它实现了 LifecycleOwner,它允许自动取消订阅,只是从 androidx.activity.ComponentActivity 源复制的,所以一切都应该没问题:D:
static class Holder extends RecyclerView.Holder implements LifecycleOwner {
/*pkg*/ LifecycleRegistry lifecycle = new LifecycleRegistry(this);
/*pkg*/ Holder(ViewGroup parent) { /* creating holder using parent's context */ }
/*pkg*/ void bind(ItemViewModel viewModel) {
viewModel.getItem().observe(this, binding.text1::setText);
}
@Override public Lifecycle getLifecycle() { return lifecycle; }
}
List view-model,"classique" androidx-ish ViewModel,但是很粗糙,也提供嵌套视图模型。请注意,在此示例中,所有视图模型在构造函数中立即开始运行,直到命令清除父视图模型!不要在家里尝试这个!
public class ItemListViewModel extends ViewModel {
private final MutableLiveData<List<ItemViewModel>> items = new MutableLiveData<>();
public ItemListViewModel() {
final List<String> list = Items.getInstance().getItems();
// create "nested" view-models which start background job immediately
final List<ItemViewModel> itemsViewModels = list.stream()
.map(ItemViewModel::new)
.collect(Collectors.toList());
items.setValue(itemsViewModels);
}
public LiveData<List<ItemViewModel>> getItems() { return items; }
@Override protected void onCleared() {
// need to clean nested view-models, otherwise...
items.getValue().stream().forEach(ItemViewModel::cancel);
}
}
Item 的视图模型,使用一点 rxJava 来模拟一些后台工作和更新。我故意不将其实现为androidx....ViewModel,只是为了强调视图模型不是谷歌命名的视图模型,而是表现为视图模型。但在实际程序中,它很可能会扩展:
// Wow, we can implement ViewModel without androidx.lifecycle.ViewModel, that's cool!
public class ItemViewModel {
private final MutableLiveData<String> item = new MutableLiveData<>();
private final AtomicReference<Disposable> work = new AtomicReference<>();
public ItemViewModel(String topicInitial) {
item.setValue(topicInitial);
// start updating ViewModel right now :D
DisposableHelper.set(work, Observable
.interval((long) (Math.random() * 5 + 1), TimeUnit.SECONDS)
.map(i -> topicInitial + " " + (int) (Math.random() * 100) )
.subscribe(item::postValue));
}
public LiveData<String> getItem() { return item; }
public void cancel() {
DisposableHelper.dispose(work);
}
}
本示例中的一些注释:
- “父”视图模型存在于活动范围内,因此它的所有数据(嵌套视图模型)也是如此。
- 在本例中,所有嵌套的虚拟机立即开始运行。这不是我们想要的。我们要相应地修改构造函数、onBind、onRecycle 和相关方法。
- 请对内存泄漏进行测试。