先说点题外话,最近报名了简书的日更计划,所以每天都会写一篇文章,字数不会太多,但是内容都会经过认真准备的,希望大家也能从中学到一些有用的知识,一起进步。
今天介绍的是谷歌推荐的 MVP
写法,它的项目地址是 android-architecture。
我对它的主要代码结构做了一个截图,今天就下面这种图来认识一下规范的MVP
写法。
从图中我们可以看出它的代码结构是
按功能模块来分包
数据是放在data
下,工具类是放在utils
下。
它使用的 MVP 架构通过阅读源码可以知道是如下的架构:
谷歌例子中的 MVP 架构
根据上边的代码结构和截图,今天就从以下几个方面来分析:
- 第一部分是数据部分在
data
包下 - 第二部分是视图和中间人部分在
对应的功能模块
包下 - 第三部分是
BaseView
和BasePresenter
- 最后是阅读别人代码学到的一些编程思路
第一部分也就是数据部分
在 MVP
中的M
表示的是Model
也就是数据。查看谷歌这个例子中,它的model
是这样去定义的,先定义model
的接口,然后再定义接口的具体实现类。
这也属于面向对象开发的思路,先把对象的属性和对应的方法,也就是先做设计,然后再去做具体的开发。
简单贴一下例子中的源码,具体源码可点击查看:
接口定义
public interface TasksDataSource {
interface LoadTasksCallback {
void onTasksLoaded(List<Task> tasks);
void onDataNotAvailable();
}
...
void completeTask(@NonNull Task task);
void completeTask(@NonNull String taskId);
...
void deleteTask(@NonNull String taskId);
}
接口实现
public class TasksRepository implements TasksDataSource {
...
// Prevent direct instantiation.
private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
@NonNull TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
...
}
Model
中主要做的就是数据的增删改查操作
,上面的代码中有一个checkNotNull
方法,该方法在support
包下也有,我这里贴一下源码看看源码中是如何判空的
public static <T> T checkNotNull(T reference) {
if (reference == null) {
throw new NullPointerException();
}
return reference;
}
看完之后是不是觉得很简单,但是我们平时就是想不到,所以多阅读别人优秀的源码是多么重要啊。
第二部分是视图(View)和 中间人(Presenter)
在这里它使用了一个契约类 TasksContract
来存放视图和中间人。这样做可以减少类的创建,便于管理和维护。在该契约类中主要是设计View
要中要实现的方法和Presenter
中要实现的方法,也属于面向对象中的先设计后再具体实现。
简要代码如下,具体代码可点击查看:
public interface TasksContract {
interface View extends BaseView<Presenter> {
void setLoadingIndicator(boolean active);
...
void showCompletedFilterLabel();
...
void showFilteringPopUpMenu();
}
interface Presenter extends BasePresenter {
void result(int requestCode, int resultCode);
...
void completeTask(@NonNull Task completedTask);
...
TasksFilterType getFiltering();
}
}
设计完了接口下面就是具体的实现了。再贴一下开头的MVP
结构图
再看一下 TasksPresenter
的实现:这块是核心,理解了这里就基本上理解了这个例子中的 MVP 架构
public class TasksPresenter implements TasksContract.Presenter {
// 这里是 我们前面定义的 Model 具体实现
private final TasksRepository mTasksRepository;
// 这里是 我们前面定义的 View 接口
private final TasksContract.View mTasksView;
// 这里是 通过构造方法将 Model 和 View 注入到 Presenter 中
public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
mTasksView = checkNotNull(tasksView, "tasksView cannot be null!");
// 这里通过接口注入的方式将 Presenter 注入到 View 中,这里的 setPresenter 是 BaseView 接口中的 方法
mTasksView.setPresenter(this);
}
// 所以这就对应上了上图的框架结构图
...
}
...
}
上面的代码中讲到了构造方法注入和接口注入,是为了实现类和类之间的解耦,用到的两种依赖注入
手段。这个知识点我还在整理中过几天也会跟进完善。
以上就是谷歌MVP
例子的简单理解,网上有很多很详细的介绍博客,想深入了解的可以去搜一下。
在阅读以上源码的时候我还发现了Activity中是不做UI操作的,都放到 Fragment 中去实现,我想了下,这可能也是一种好的开发习惯吧,因为Fragment
在平板上兼容性会更好一些。网上说更好的方式是动态去添加 Fragment
这里由于只有一个页面,所以它直接在activity 的 xml 文件中写死了Fragment
这是它将Fragment
添加到activity
中的一个工具类,在添加之前还做了一层判空操作,真的是好习惯。
public class ActivityUtils {
/**
* The {@code fragment} is added to the container view with id {@code frameId}. The operation is
* performed by the {@code fragmentManager}.
*
*/
public static void addFragmentToActivity (@NonNull FragmentManager fragmentManager,
@NonNull Fragment fragment, int frameId) {
checkNotNull(fragmentManager);
checkNotNull(fragment);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(frameId, fragment);
transaction.commit();
}
}
而且它在Activity
中获取该Fragment
实例的时候又做了一次判空操作。如下:
TasksFragment tasksFragment =
(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
// Create the fragment
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity(
getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}
文章到这里就结束了。
阅读这个例子不仅看懂了 MVP
架构,还学到了一些别人写代码的好习惯,以后得多多阅读别人优秀的代码了,加油!
网友评论