Android架构组件:用ViewModelCommandLiv
2018-06-08 本文已影响46人
你么看也不要你
如果你已经开始使用Google官方的Mvvm框架,理想情况下,Activity或者Fragment当中的逻辑应该越少越好,然而事实上我们的页面在加载layout,获取Viewmodel之外,往往还负责着以下一些职责:
- 各种对话框(等待效果,确认,Toast等等)
- 页面跳转
- 其他页面需要监控viewmodel变化的行为
尽管DataBinding解决了数据->视图绑定的问题,仍然有一些操作是我们必须在Activity/Fragment当中完成的,这些不得不由视图完成的行为可以视为一系列CallBack,现在的问题是,如何将这些CallBack从ViewModel中分发出去。
Mvp中如何解决Callback问题
MVP模式下,会约定一个约束类,来要求View跟Presenter分别负责自己的行为:
public interface BaseMVPContract {
interface View extends BaseView {
/**
* 开始等待加载
*/
void onLoadingStart();
/**
* 结束等待加载
*/
void onLoadingEnd();
}
interface Presenter extends Presenter<View> {
/**
* 加载ProjectList
*
* @param parentId 父分类id
* @param name 分类名称
*/
void loadProjects(Long Id);
}
}
在Mvvm中,ViewModel不关心View的实现,也不持有View的引用,因此,对View的约束没有存在的必要,参考LiveData的使用,我们可以考虑将Viewmodel要求视图执行的指令封装成一个被观察者(LiveData),将动作发布出去,视图方订阅这个LiveData来实现自己的指令执行逻辑.
封装指令LiveData的一种实现方式
public class ViewModelCommandLiveData<T> {
private MutableLiveData<List<UiAction<T>>> delegate = new MutableLiveData<>();
private List<UiAction<T>> list = new ArrayList<>();
@MainThread
public void execute(@NonNull UiAction<T> action) {
list.add(action);
delegate.setValue(list);
//一个指令的状态是瞬时的,因此在执行完成之后需要清除状态
delegate.setValue(null);
}
public void observe(LifecycleOwner owner, @NonNull UiActionExecutor<T> executor) {
delegate.observe(owner, uiActions -> {
if (uiActions != null) {
for (UiAction<T> action : uiActions) {
executor.exec(action);
}
}
if (uiActions != null && !uiActions.isEmpty()) {
list = new ArrayList<>();
}
});
}
public interface UiAction<E> {
void exec(E t);
}
public interface UiActionExecutor<R> {
void exec(UiAction<R> action);
}
}
ViewModel中如何发送指令
public class ProjectListViewModel extends AndroidViewModel {
//指令LiveData
public ViewModelCommandLiveData<ProjectListVmHandler> commands = new ViewModelCommandLiveData<>();
public LiveData<List<Project>> getProjectList(String userId) {
//发送指令开始等待效果
commands.execute(t->t.onLoadingStart());
final MutableLiveData<List<Project>> data = new MutableLiveData<>();
gitHubService.getProjectList(userId).enqueue(new Callback<List<Project>>() {
@Override
public void onResponse(Call<List<Project>> call, Response<List<Project>> response) {
//发送指令关闭等待效果
commands.execute(t->t.onLoadingEnd());
data.setValue(response.body());
}
@Override
public void onFailure(Call<List<Project>> call, Throwable t) {
//发送指令关闭等待效果
commands.execute(t->t.onLoadingEnd());
data.setValue(null);
}
});
return data;
}
public interface ProjectListVmHandler{
void onLoadingStart();
void onLoadingEnd();
}
}
视图中订阅指令并实现指令处理:
public class ProjectListFragment extends Fragment implements Injectable {
public static final String TAG = "ProjectListFragment";
@Inject
ViewModelProvider.Factory viewModelFactory;
private ProjectListViewModel.ProjectListVmHandler mHandler = new ProjectListViewModel.ProjectListVmHandler() {
@Override
public void onLoadingStart() {
showLoading();
}
@Override
public void onLoadingEnd() {
hideLoading();
}
};
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final ProjectListViewModel viewModel = ViewModelProviders.of(this,
viewModelFactory).get(ProjectListViewModel.class);
observeViewModel(viewModel);
}
private void observeViewModel(ProjectListViewModel viewModel) {
//订阅viewModel指令LiveData
viewModel.commands.observe(this, action -> action.exec(mHandler));
}
}
总结
关于ViewModel中是否需要CallBack的话题,不同的业务场景有不同的回答,作为一个程序员,我们总是希望把UI相关的重复、繁琐又容易出bug的套路代码交给代码生成去处理,但是实际业务场景往往需要我们这样去做,本文的VMCommandLiveData作为一个解决思路,希望能起到抛砖引玉的作用。