不确定Google是否支持嵌套的ViewModel,看起来似乎不支持。
幸运的是,在需要应用MVVM模式时我们并不需要坚持使用androidx.lifecycle.ViewModel
。这里有一个我决定编写的小例子:
Fragment,不做任何更改:
@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 除了填充视图之外,还负责通知项目的观察者 - 它们是否应继续监听。在我的示例适配器中,使用的是扩展 RecyclerView.Adapter 的 ListAdapter,因此会接收项目列表。这是无意中的,我只是编辑了一些我已经有的代码。但为了演示目的,这也是可以接受的。
@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);
}
@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 {
LifecycleRegistry lifecycle = new LifecycleRegistry(this);
Holder(ViewGroup parent) { }
void bind(ItemViewModel viewModel) {
viewModel.getItem().observe(this, binding.text1::setText);
}
@Override public Lifecycle getLifecycle() { return lifecycle; }
}
List view-model 是一种经典的 AndroidX ViewModel,但非常粗糙,还提供了嵌套的 View Model。请注意,在此示例中所有的 View Model 都会立即在构造函数中开始运行,直到父 View Model 被命令清除!请不要在家里尝试这样做!
public class ItemListViewModel extends ViewModel {
private final MutableLiveData<List<ItemViewModel>> items = new MutableLiveData<>();
public ItemListViewModel() {
final List<String> list = Items.getInstance().getItems();
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() {
items.getValue().stream().forEach(ItemViewModel::cancel);
}
}
条目的视图模型,使用一些 rxJava 模拟后台工作和更新。故意不将其实现为 androidx....ViewModel
,只是为了强调视图模型并不是 Google 命名的 ViewModel,而是表现为视图模型的东西。在实际程序中,它很可能会被扩展:
public class ItemViewModel {
private final MutableLiveData<String> item = new MutableLiveData<>();
private final AtomicReference<Disposable> work = new AtomicReference<>();
public ItemViewModel(String topicInitial) {
item.setValue(topicInitial);
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);
}
}
在此示例中有几点需要注意:
- "父" ViewModel 存在于 activity 的作用域中,因此所有其数据(嵌套的 view models)也存在于该作用域中。
- 在此示例中,所有 嵌套的视图模型都会立即开始运行。这不是我们想要的结果。我们需要相应地修改构造函数、onBind、onRecycle 和相关方法。
- 请务必测试其是否存在内存泄漏问题。