最近在看关于MVP框架的封装,于是网上搜了一下,发现了谷歌官方的MVP Demo,发现还是Google的看着顺眼,上图为Demo的框架图。于是就对其分析了一下。Google的MVP版本很多,不过思想都一样,所以就拿当前较火的RxJava那个MVP版本进行分析。
接口类
我们挑其中的一个addedittask模块来看,可以看到里面有个叫AddEditTaskContract的类,这个类是干嘛用的呢? 这就是和普通的MVP结构不同的地方:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public interface AddEditTaskContract { interface View extends BaseView<Presenter> { void showEmptyTaskError(); void showTasksList(); void setTitle(String title); void setDescription(String description); boolean isActive(); } interface Presenter extends BasePresenter { void saveTask(String title, String description); void populateTask(); } } |
Contract字面意思是契约,这个契约类的作用是把View层和Presenter层的接口包装起来,方便管理,同时也减少了接口文件的数量。BaseView传了个Presenter的泛型进去,我们接下来看看BaseView和BasePresenter:
1 2 3 4 5 | public interface BaseView<T> { void setPresenter(T presenter); } |
1 2 3 4 5 6 7 | public interface BasePresenter { void subscribe(); void unsubscribe(); } |
BaseView中只有一个方法setPresenter,这个方法的作用是获取Presenter对象,方便在View层使用(如在Activity和Fragment中)。看见BasePresenter中的两个方法,是不是就想到了RxJava里的订阅和解除订阅?没错,在这里还真的是这个意思。
下面,就开始介绍它们的实现类了~
接口的实现
Presenter实现类
首先是实现了AddEditTaskContract.Presenter接口的AddEditTaskPresenter类,先看参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @NonNull private final TasksDataSource mTasksRepository; @NonNull private final AddEditTaskContract.View mAddTaskView; @NonNull private final BaseSchedulerProvider mSchedulerProvider; @Nullable private String mTaskId; @NonNull private CompositeSubscription mSubscriptions; |
其中TasksDataSource是数据源,AddEditTaskContract.View是View对象,BaseSchedulerProvider是订阅提供者(用于线程切换),CompositeSubscription可以理解为RxJava的订阅管理者,在适当的时候解除订阅防止内存泄漏。
然后看构造方法:
1 2 3 4 5 6 7 8 9 10 11 12 | 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); } |
构造方法传进来了三个主要的参数,同时初始化了CompositeSubscription。注意看,上面BaseView中提到的setPresenter方法在这里用到了,就是获取当前对象,也就是Presenter对象。至于构造方法中传进来的对象怎么来的,我们等下在View层会详细讲。
剩下的就是接口实现的方法了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | @Override public void subscribe() { if (mTaskId != null) { populateTask(); } } @Override public void unsubscribe() { mSubscriptions.clear(); } /** * 省略部分代码 */ @Override public void populateTask() { if (mTaskId == null) { throw new RuntimeException("populateTask() was called but task is new."); } Subscription subscription = mTasksRepository .getTask(mTaskId) .subscribeOn(mSchedulerProvider.computation()) .observeOn(mSchedulerProvider.ui()) .subscribe(new Observer<Task>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { if (mAddTaskView.isActive()) { mAddTaskView.showEmptyTaskError(); } } @Override public void onNext(Task task) { if (mAddTaskView.isActive()) { mAddTaskView.setTitle(task.getTitle()); mAddTaskView.setDescription(task.getDescription()); } } }); mSubscriptions.add(subscription); } |
我们主要看subscribe、unsubscribe和populateTask方法。在subscribe方法中进行初始化数据,调用了populateTask方法添加订阅和获取数据。unsubscribe方法中进行解除订阅。在populateTask方法中,返回数据的地方调用了View的接口,将数据传给View层。至此,整个Presenter算是讲完啦。我们接下来看到View层,也就是Activity或者Fragment。
View实现类
先看代码,下面的代码我们只看重要的部分,所以省略了很多,想看完整的可以下载Google官方完整的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View { private AddEditTaskContract.Presenter mPresenter; /*...*/ @Override public void onResume() { super.onResume(); mPresenter.subscribe(); } @Override public void onPause() { super.onPause(); mPresenter.unsubscribe(); } @Override public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) { mPresenter = checkNotNull(presenter); } /*...*/ @Override public void showEmptyTaskError() { Snackbar.make(mTitle, getString(R.string.empty_task_message), Snackbar.LENGTH_LONG).show(); } @Override public void showTasksList() { getActivity().setResult(Activity.RESULT_OK); getActivity().finish(); } @Override public void setTitle(String title) { mTitle.setText(title); } @Override public void setDescription(String description) { mDescription.setText(description); } @Override public boolean isActive() { return isAdded(); } } |
首先,AddEditTaskFragment实现了AddEditTaskContract.View接口。然后在onResume和onPause这两个关系生命周期的地方进行了和Presenter的绑定(防止内存泄漏),在setPresenter方法中获取Presenter实例。再看下面一堆的View接口中定义的方法,就是得到数据后用来进行界面操作了啦~
Activity
上面说到AddEditTaskPresenter的构造方法中的参数怎么来的,因为是RxJava的MVP版本,就顺带说一下吧。因为整个工程是多Activity+多Fragment的结构,其实是在Activity中创建了Fragment对象,然后添加到布局中的。我们看一下AddEditTaskActivity的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public class AddEditTaskActivity extends AppCompatActivity { public static final int REQUEST_ADD_TASK = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.addtask_act); /*...*/ AddEditTaskFragment addEditTaskFragment = (AddEditTaskFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); String taskId = null; if (addEditTaskFragment == null) { addEditTaskFragment = AddEditTaskFragment.newInstance(); if (getIntent().hasExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID)) { taskId = getIntent().getStringExtra( AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID); actionBar.setTitle(R.string.edit_task); } else { actionBar.setTitle(R.string.add_task); } ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), addEditTaskFragment, R.id.contentFrame); } // Create the presenter new AddEditTaskPresenter( taskId, Injection.provideTasksRepository(getApplicationContext()), addEditTaskFragment, Injection.provideSchedulerProvider()); } /*...*/ } |
创建了AddEditTaskFragment对象,然后添加到布局中。最后new了一个AddEditTaskPresenter,这才是重点。到这里,不禁有一个问题,AddEditTaskPresenter构造方法的第二个和第四个参数是哪来的?我们再看Injection类就知道了:
1 2 3 4 5 6 7 8 9 10 11 12 | public class Injection { public static TasksRepository provideTasksRepository(@NonNull Context context) { checkNotNull(context); return TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(), TasksLocalDataSource.getInstance(context, provideSchedulerProvider())); } public static BaseSchedulerProvider provideSchedulerProvider() { return SchedulerProvider.getInstance(); } } |
文章都快看完了,都说MVP,Model层在哪里呢?看上面代码中的provideTasksRepository方法,其实这就相当于Model层,获取到数据后传给Presenter,只不过用了一个数据源的名字,功能都一样。provideSchedulerProvider方法是获取一个RxJava的线程调度器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public class SchedulerProvider implements BaseSchedulerProvider { @Nullable private static SchedulerProvider INSTANCE; // Prevent direct instantiation. private SchedulerProvider() { } public static synchronized SchedulerProvider getInstance() { if (INSTANCE == null) { INSTANCE = new SchedulerProvider(); } return INSTANCE; } @Override @NonNull public Scheduler computation() { return Schedulers.computation(); } @Override @NonNull public Scheduler io() { return Schedulers.io(); } @Override @NonNull public Scheduler ui() { return AndroidSchedulers.mainThread(); } } |
这个类用了懒汉式获取单例(看源码还是有收获的^_^),实现了BaseSchedulerProvider接口:
1 2 3 4 5 6 7 8 9 10 11 | public interface BaseSchedulerProvider { @NonNull Scheduler computation(); @NonNull Scheduler io(); @NonNull Scheduler ui(); } |
一共三个线程,计算线程、IO线程、UI线程,完成了对RxJava线程管理的封装使用。
总结
通过这次解读,收益良多。目前来说,MVP确实是个好框架,而且RxJava便捷的线程切换对于在Model层中进行IO操作和Presenter层的UI操作的支持,两者简直是天生一对。
对于Google官方MVP框架的源码解读到这就结束啦,下一篇将对MVP进行封装一下,让这个框架更加实用,同时简化操作,少写代码。敬请期待~