Android爬楼梯android_它山之石Android开发

(新瓶旧酒)谷歌官方MVP项目学习--浅入源码

2017-09-30  本文已影响1061人  hongjay

人笨学的慢啊。。抓紧学习MVP

网上MVP的学习资料也是多如牛毛,来看看谷歌爸爸是怎么教我们MVP的吧

项目介绍


Google把这个项目命名为:Android架构蓝图。

这个项目也是金诚先生推荐的2017年Android百大框架排行榜中的一个

android-architecture
一句话介绍:google提供的Android当下各种基本框架
上榜理由:看完它,mvp,mvvm都将入切瓜砍菜,秋风扫落叶一般...
github https://github.com/googlesamples/android-architecture
作者:google

项目的目的是通过展示各种架构app的不同方式来帮助开发者解决架构问题。项目中通过不同的架构概念及方式实现了功能相同的app。你可以用示例来当做参考,或是干脆拿来当做创建app项目的基础。项目中,希望大家能把关注点集中到代码结构、整体架构、可测试性、可维护性这四个方面。当然实现app有很多种方式,千万不要把它当做定式。

项目中有哪些示例


目前已经稳定的示例有

仍在进展中的示例有

显而易见的,本渣只能从todo-mvp(mvp基础架构示例)项目中探索谷歌爸爸告诉我们的,对MVP架构的最本源的揭示。

todo-mvp

应用程序的名字是todo-mvp(待办清单-mvp),为此项目中的其他示例提供了基础。该样本旨在:

todo-mvp示例使用以下依赖关系:

设计app


该应用程序由四个UI页面组成:

在这个应用程序以及其他基于它的版本中,每个功能页面都使用以下类和接口:

曾经的架构

追溯到2012年我们的代码库使用的是基本结构,那个时候我们没有使用任何第三方网络类库,而且AsyncTask也是我们的好朋友。当时的架构可以大致表示为下图。


代码被划分为两层结构:

APIProvider提供了一些方法,使Activity和Fragment能够很容易的实现与REST API的数据交互。这些方法使用URLConnection和AsyncTask在一个单独的线程内执行网络请求,然后通过回调将结果返回给Activity。

按照同样的方式,CacheProvider 所包含的方法负责从SharedPreferences和SQLite数据库检索和存储数据。同样使用回调的方式,将结果传回Activity。

存在的问题:

MVP架构

MVP 是如何建立起关系来的?

首先,M 只在 P 中使用,与 V 无关,因此 M 只要传入 P 中即可。
P 与 V 之间的关系是这样的:V 和 P 互相保存对方的实例。V 在需要进行数据操作逻辑的时候不自己做,而是交给 P 来做,P 完成之后调用 V 中的方法实现界面更新。

所以PRESENTER 的作用是承担业务逻辑和相应的UI逻辑。 而View层几乎没有任何逻辑操作,它只是将presenter 的命令转换为UI操作,并且监听用户的操作,然后传递给Presenter 。

可以看到这里的View指的是Fragment,一是因为Google推荐使用Fragment而不是Activity来显示内容,二是Fragment作为View既能解决layout作为View导致的鸡肋,也不会使Activity功能太过膨胀(这里Activity是一个总体的Controller,让Fragment和Presenter进行连接)
左边那块就是Model了,Presenter先到内存中的缓存进行查询,如果没有,才到本地数据源或者远程数据源请求

项目包结构


除BaseView、BasePresenter两个接口,其他package都以业务功能来划分的:
addedittask —— 添加任务
data —— 数据源
statistics —— 任务统计
taskdetail —— 任务详情
tasks —— 任务列表
util —— 工具类

项目MVP实现方式

这节我们就具体来看官方示例到底是如何实现mvp的。这里我们先看下总体的轮廓,关于项目中业务代码我们仅列出了添加任务页(addedittask )的相关类,其他业务代码类似。(手绘,诸兄勿弃)

基类

Presenter基类:

public interface BasePresenter {
    void start();
}

// 例子中这个start()方法都在Fragment的onResume()中调用,作用是presenter开始获取数据并调用view中方法改变界面显示。

@Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }

View基类:

public interface BaseView<T> {
    void setPresenter(T presenter);
}

BaseView中含方法setPresenter,该方法作用是在将presenter实例传入view中,其调用时机是presenter实现类的构造函数中。

Contract 契约类

不同于其他的MVP项目,官方的MVP架构中都定义有xxContract契约类,把P层和V层的接口统一写在契约类中,能够更清晰的看到在Presenter层和View层中有哪些功能,方便我们以后的维护。

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();

        boolean isDataMissing();
    }
}

addedittask功能模块分析


来具体看一个模块

AddEditTaskActivity —— Activity
AddEditTaskContract —— 在这里定义了两个子接口View和Presenter,和他们的方法。实现了BaseView、BasePresenter两个接口
AddEditTaskFragment —— 实现AddEditTaskContract.View接口
AddEditTaskPresenter —— 实现AddEditTaskContract.Presenter接口

从Acitivty入手
public class AddEditTaskActivity extends AppCompatActivity {
  protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.addtask_act);

        // Set up the toolbar.
        ...
        // Add Fragment  
        ...
       // Create the presenter
        mAddEditTaskPresenter = new AddEditTaskPresenter(
                taskId,
                Injection.provideTasksRepository(getApplicationContext()),
                addEditTaskFragment,
                shouldLoadDataFromRepo);
     }
}

我们可以看到一共做了三件事,初始化toolbar,添加fragment,创建一个presenter
在创建presenter的时候,把fragment传了进去,实现了V和P的绑定
因此Activity在项目中是一个全局的控制者,负责创建view以及presenter实例,并将二者联系起来。

View实现

AddEditTaskFragment 中通过实现BaseView中的setPresenter(),将Presenter和View关联起来。

@Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }

    @Override
    public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
        mPresenter = checkNotNull(presenter);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        //fab属于view控件,也归fragment控制
        FloatingActionButton fab =
                (FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task_done);
        fab.setImageResource(R.drawable.ic_done);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //响应点击事件 分发给P层
                mPresenter.saveTask(mTitle.getText().toString(), mDescription.getText().toString());
            }
        });
    }

@Override
public boolean isActive() {
  return isAdded();
}

Presenter实现

AddEditTaskPresenter是AddEditTaskContract.Presenter的实现

public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
            @NonNull AddEditTaskContract.View addTaskView, boolean shouldLoadDataFromRepo) {
        mTaskId = taskId;
        mTasksRepository = checkNotNull(tasksRepository);
        mAddTaskView = checkNotNull(addTaskView);
        mIsDataMissing = shouldLoadDataFromRepo;

        mAddTaskView.setPresenter(this);
    }
@Override
    public void start() {
        if (!isNewTask() && mIsDataMissing) {
            populateTask();
        }
    }
@Override
    public void populateTask() {
        if (isNewTask()) {
            throw new RuntimeException("populateTask() was called but task is new.");
        }
        mTasksRepository.getTask(mTaskId, this);
    }

Model实现细节

该项目中Model层最大的特点是被赋予了数据获取的职责,与我们平常Model层只定义实体对象截然不同。实例中,数据的获取、存储、数据状态变化都是Model层的任务,Presenter会根据需要调用该层的数据处理逻辑并在需要时将回调传入。

TasksDataSource是一个接口. 接口中定义了Presenter查询数据的回调接口, 还有一些增删改查的方法。

public interface TasksDataSource {
    // 加载数据的回调
    interface LoadTasksCallback {
        //成功回调
        void onTasksLoaded(List<Task> tasks);
        //失败回调
        void onDataNotAvailable();
    }
    // 获取数据的回调
    interface GetTaskCallback {

        void onTaskLoaded(Task task);

        void onDataNotAvailable();
    }

    void getTasks(@NonNull LoadTasksCallback callback);
    //通过id获得数据
    void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
    //保存task
    void saveTask(@NonNull Task task);
    ...
}
public class Injection {

    public static TasksRepository provideTasksRepository(@NonNull Context context) {
        checkNotNull(context);
        return TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(),
                TasksLocalDataSource.getInstance(context));
    }
}

TasksRepository的构造如下:

private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
                        @NonNull TasksDataSource tasksLocalDataSource) {
    mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
    mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
public void getTasks(@NonNull final LoadTasksCallback callback) {
        // 判空处理
        checkNotNull(callback);

        // 从内存缓存中读取数据
        if (mCachedTasks != null && !mCacheIsDirty) {
            callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
            return;
        }

        if (mCacheIsDirty) {
            // 如果缓存是脏数据,那么从网络获取数据
            getTasksFromRemoteDataSource(callback);
        } else {
            // 查询本地存储(如果有)。 如果没有,查询网络。
            mTasksLocalDataSource.getTasks(new LoadTasksCallback() {
                @Override
                public void onTasksLoaded(List<Task> tasks) {
                    refreshCache(tasks);
                    //在P中通过回调得到M层查询到的数据
                    callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
                }

                @Override
                public void onDataNotAvailable() {
                    getTasksFromRemoteDataSource(callback);
                }
            });
        }
    }

总结


回顾整理,总结一下:

看完发现,好像还真的是一大堆的接口啊!!!

参考文章
http://www.jianshu.com/p/d54f82848bea
http://blog.csdn.net/jjwwmlp456/article/details/54869179
http://blog.csdn.net/u013400743/article/details/52205445
http://www.jianshu.com/p/8ca27934c6e6
http://blog.csdn.net/card361401376/article/details/51518605
https://mp.weixin.qq.com/s?__biz=MzA4MjA0MTc4NQ==&mid=404088059&idx=3&sn=78dafacbca09b0d7345344c3eef24aff#rd
http://www.jianshu.com/p/0bf5289ee8ce
http://www.jianshu.com/p/389c9ae1a82c
http://blog.csdn.net/ljd2038/article/details/51477475

上一篇下一篇

猜你喜欢

热点阅读