GitHub 标星 9K 的 Google 官方 MVP+Rxjava 项目详解,靠这份资料我从 6K 变成了 40K
private TextView mTitle;
private TextView mDescription;
public static AddEditTaskFragment newInstance() {return new AddEditTaskFragment();}
@Overridepublic void onResume() {super.onResume();mPresenter.subscribe();}
@Overridepublic void onPause() {super.onPause();mPresenter.unsubscribe();}
@Overridepublic void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {mPresenter = checkNotNull(presenter);}
@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);
FloatingActionButton fab =(FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task_done);fab.setImageResource(R.drawable.ic_done);fab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mPresenter.saveTask(mTitle.getText().toString(),mDescription.getText().toString());}});}
@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View root = inflater.inflate(R.layout.addtask_frag, container, false);mTitle = (TextView) root.findViewById(R.id.add_task_title);mDescription = (TextView) root.findViewById(R.id.add_task_description);
setHasOptionsMenu(true);setRetainInstance(true);return root;}
@Overridepublic void showEmptyTaskError() {Snackbar.make(mTitle, getString(R.string.empty_task_message), Snackbar.LENGTH_LONG).show();}
@Overridepublic void showTasksList() {getActivity().setResult(Activity.RESULT_OK);getActivity().finish();}
@Overridepublic void setTitle(String title) {mTitle.setText(title);}
@Overridepublic void setDescription(String description) {mDescription.setText(description);}
@Overridepublic boolean isActive() {return isAdded();}}
这里的关注重点除了上面提到的订阅和取消订阅方法。还应当有 presenter 实例的获取。通过 setPresenter 方法。
4.3.6AddEditTaskPresenter
在 presenter 的实现类中,可以对 Model 数据进行操作。实例中,数据的获取、存储、数据状态变化都是 model 层的任务,presenter 会根据需要调用该层的数据处理逻辑并在需要时将回调传入。这样 model、presenter、view 都只处理各自的任务,此种实现确实是单一职责最好的诠释。
/**
AddEditTaskPresenter 监听用户的 UI 操作,获取数据,更新界面
需要实现的方法有除了{@link BasePresenter} 中的{@link #subscribe()},{@link #unsubscribe()}
还有{@link AddEditTaskContract}中的{@link #saveTask(String, String)} ()},{@link #populateTask()},*/public class AddEditTaskPresenter implements AddEditTaskContract.Presenter {
@NonNullprivate final TasksDataSource mTasksRepository;//数据 model 来源
@NonNullprivate final AddEditTaskContract.View mAddTaskView;//MVP 中的 view
@NonNullprivate final BaseSchedulerProvider mSchedulerProvider;//提供 Scheduler 用于 Rxjava 线程调度
@Nullableprivate String mTaskId;//task 的 id
@NonNullprivate CompositeSubscription mSubscriptions;//CompositeSubscription 持有所有的 Subscriptions
/**
创建添加和编辑 task 的对应 presenter
@param taskId 需要编辑的 task 的 id,如果为新建 task 为空
@param tasksRepository 是所有 task 数据的入口
@param addTaskView 添加/编辑 界面*/public AddEditTaskPresenter(@Nullable String taskId,@NonNull TasksDataSource tasksRepository,@NonNull AddEditTaskContract.View addTaskView,@NonNull BaseSchedulerProvider schedulerProvider) {mTaskId = taskId;mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");mAddTaskView = checkNotNull(addTaskView, "addTaskView cannot be null!");mSchedulerProvider = checkNotNull(schedulerProvider, "schedulerProvider cannot be null!");
mSubscriptions = new CompositeSubscription();mAddTaskView.setPresenter(this);}
@Overridepublic void subscribe() {if (mTaskId != null) {populateTask();}}
@Overridepublic void unsubscribe() {mSubscriptions.clear();}
@Overridepublic void saveTask(String title, String description) {Task newTask = mTaskId == null ?new Task(title, description) :new Task(title, description, mTaskId);saveTask(newTask);}
/**
直接通过 TasksRepository 保存 task
@param task*/private void saveTask(@NonNull Task task) {if (task.isEmpty()) {mAddTaskView.showEmptyTaskError();} else {mTasksRepository.saveTask(task);mAddTaskView.showTasksList();}}
/**
根据 id 获取 task*/@Overridepublic void populateTask() {if (mTaskId == null) {throw new RuntimeException("populateTask() was called but task is new.");}Subscription subscription = mTasksRepository.getTask(mTaskId) //获取 task.subscribeOn(mSchedulerProvider.computation()).observeOn(mSchedulerProvider.ui()).subscribe(new Observer<Task>() {@Overridepublic void onCompleted() {
}
@Overridepublic void onError(Throwable e) {if (mAddTaskView.isActive()) {mAddTaskView.showEmptyTaskError(); //调用页面出错}}
@Overridepublic void onNext(Task task) {if (mAddTaskView.isActive()) {mAddTaskView.setTitle(task.getTitle()); //页面正确显示 taskmAddTaskView.setDescription(task.getDescription());}}});
mSubscriptions.add(subscription);}}
这里我在上面的代码中添加了备注,希望尽可能的讲解清楚。这里的代码逻辑是非常的清楚地,下面再捋一捋:
1
AddEditTaskPresenter
的构造函数异常重要,这里获取了AddEditTaskContract.view
的实例对象和TaskDataSource
的实例对象,分别对应了 MVP 中得到 View 和 Model。
2
subscribe
方法和unSubscribe
方法分别对应了订阅和取消订阅状态,这在前面的AddEditTaskFragment
的onResume
和onPause
方法中进行了实现。这里分别完成了获取 task 和取消订阅两个功能。
3 在这里调用了上面提到的
setPresenter
方法,view 持有 presenter 实例的操作。
4 通过
mSubscriptions.add(subscription);
将这里获取 task 的subscription
添加到了CompositeSubscription
中。
5 通过
mTasksRepository.saveTask(task);
和mTasksRepository.getTask(mTaskId)
完成了 task 的存储和获取功能,所有 model 层的操作都交给了TasksRepository
,model 层的操作将下面详细讲解。
6 我们在
onError
和onNext
中分别完成了错误和正确页面的逻辑处理。
4.4 model 层设计代码分析
Rxjava 完成了数据层和 presenter 层的交互,这里回到上面的代码,可以看到通过 model 层的操作都交给了TasksRepository
对象。
接下来到 data 包中就是所有 model 层的操作。
这里就不在展示分析所有代码。TasksLocalDataSource 代表本地数据,TasksRemoteDataSource 代表远程数据。Task 为实体类对象,TasksRepository 为整个数据的核心漱口。这里直接展示 TasksRepository 中的 getTask 方法。
/**
Gets tasks from local data source (sqlite) unless the table is new or empty. In that case it
uses the network data source. This is done to simplify the sample.*/@Overridepublic Observable<Task> getTask(@NonNull final String taskId) {checkNotNull(taskId);
final Task cachedTask = getTaskWithId(taskId);
// Respond immediately with cache if availableif (cachedTask != null) {return Observable.just(cachedTask);}
// Load from server/persisted if needed.
// Do in memory cache update to keep the app UI up to dateif (mCachedTasks == null) {mCachedTasks = new LinkedHashMap<>();}
// Is the task in the local data source? If not, query the network.Observable<Task> localTask = getTaskWithIdFromLocalRepository(taskId);Observable<Task> remoteTask = mTasksRemoteDataSource.getTask(taskId).doOnNext(new Action1<Task>() {@Overridepublic void call(Task task) {mTasksLocalDataSource.saveTask(task);mCachedTasks.put(task.getId(), task);}});
return
Observable.concat(localTask, remoteTask).first().map(new Func1<Task, Task>() {@Overridepublic Task call(Task task) {if (task == null) {throw new NoSuchElementException("No task found with taskId " + taskId);}return task;}});}
1 需要注意的是这里 TasksLocalDataSource 使用 SqlBrite 查询数据库并且返回流式数据。
2 关于 model 层 Respository 的了解,可以看我之前的这篇文章。浅析MVP中model层设计3 SqlBrite 是对 Android 系统的 SQLiteOpenHelper 的封装,对 SQL 操作引入了响应式语义 (Rx)(用来在 RxJava 中使用)。详情可以点击[Rxjava+数据库?来用用 SqlBrite 和 SqlDelight 吧!](
) 。
评论