Android官方架构分析(一)——todo‑mvp

前言

android-architecture是google官方推出的关于Android架构设计的开源项目,相当于为开发者提供了在Android APP架构设计方面的指南,所以学习价值不言而喻。

该项目展示了多种不同的架构概念和工具来实现相同的应用程序,这里我们将挑选其中一些经典的架构方式对其Demo进行源码分析。

这一篇,我们对标准MVP架构的todo‑mvp进行分析。

项目结构

android-architecture一系列Demo都实现了一个TODO-LIST项目,分为任务列表(tasks)、任务详情(taskdetail)、添加任务(addedittask)、任务统计(statistics)四个功能模块。

从下图分包情况也可以看出,todo‑mvp项目是按功能模块划分包名,清晰易懂。

image
image
  • addedittask:添加任务
  • data:主要对数据来源实现了封装
  • statistics:任务统计
  • taskdetail:任务详情
  • tasks:任务列表
  • util:帮助类
  • BasePresenter:Presenter基类
  • BaseView:View基类

架构思路

标准MVP架构有别于Android传统MVC架构最明显的地方在于,View与Model不发生联系,通过Presenter传递。而View不再承担逻辑功能,只负责显示视图页面,逻辑由Presenter进行处理。

image
image

在todo‑mvp项目中,每一个具体模块都由Model、Presenter、View三部分来实现。

  • Model:data包内的数据来源的封装实现类,分为本地来源和远程来源。
  • Presenter:由Activity创建Presenter实例对象。
  • View:由Activity创建,并由Fragment作为View的具体实现。
image
image

上图为官方给出的结构图,清晰的展示了MVP各部分的具体实现。

代码分析

在MVP模式中,每一个功能模块都由Model、Presenter、View组成,我们用addedittask模块来展示代码。

image
image

AddEditTaskContract类:

/**
 * This specifies the contract between the view and the presenter.
 */
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();
    }
}

Contract类作为View和presenter之间的协议,封装了View和Presenter接口。MVP模式下,分出View接口和Persenter接口主要是为了将View和Persenter通过接口的方式解耦,而Contract将两者封装到一起,又可以方便查找和修改。

View接口中封装了对页面展示绘制渲染等相关操作,Persenter接口中封装了对程序逻辑、对接Model等相关操作。View接口和Presenter接口又分别继承了BaseView和BasePresenter。

public interface BaseView<T> {

    void setPresenter(T presenter);

}

BaseView中需要保持对Presenter的引用。

public interface BasePresenter {

    void start();

}

BasePresenter中的start方法主要用于一些初始化操作。

AddEditTaskActivity类中创建AddEditTaskPresenter实例:

    // Create the presenter
    mAddEditTaskPresenter = new AddEditTaskPresenter(
            taskId,
            Injection.provideTasksRepository(getApplicationContext()),
            addEditTaskFragment,
            shouldLoadDataFromRepo);

Activity在MVC模式下,需要承担View和Controller两者的职责,代码相当繁重。而在MVP模式下,不再承担过多的功能。创建Presenter时传入Model和View的实例,具体逻辑操作都由Presenter来负责。

Presenter

public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
        TasksDataSource.GetTaskCallback {
        
    @NonNull
    private final TasksDataSource mTasksRepository;

    @NonNull
    private final AddEditTaskContract.View mAddTaskView;

    @Nullable
    private String mTaskId;

    private boolean mIsDataMissing;

    /**
     * Creates a presenter for the add/edit view.
     *
     * @param taskId ID of the task to edit or null for a new task
     * @param tasksRepository a repository of data for tasks
     * @param addTaskView the add/edit view
     * @param shouldLoadDataFromRepo whether data needs to be loaded or not (for config changes)
     */
    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);    //将Presenter实例对象赋给View
    }

    @Override
    public void start() {
        if (!isNewTask() && mIsDataMissing) {
            populateTask();
        }
    }
        
    ...
    
}

AddEditTaskPresenter实现了AddEditTaskContract.Presenter接口,并在构造函数中获取了Model和View的实例,具体业务逻辑就是对这两个实例对象进行操作来实现。

mTasksRepository:相当于Model的实例对象,对数据操作的具体实现。

mAddTaskView:相当于View的实例对象,页面展示的具体实现。

start()方法中执行了对Model对象(mTasksRepository)赋值和初始化工作。

View

public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {

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

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

AddEditTaskFragment实现了AddEditTaskContract.View接口,通过setPresenter方法获得Presenter实例,在onResume方法中执行Presenter的初始化操作,而当View接收到触发事件时,由Presenter实例来负责响应逻辑。

Model

public class TasksRepository implements TasksDataSource {

    private static TasksRepository INSTANCE = null;

    private final TasksDataSource mTasksRemoteDataSource;

    private final TasksDataSource mTasksLocalDataSource;

    // Prevent direct instantiation.
    private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
                            @NonNull TasksDataSource tasksLocalDataSource) {
        mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
        mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
    }
    
    ...
    
}

TasksRepository作为Model层的实现类,实现了TasksDataSource接口,封装了本地数据和远程数据两种数据来源,通过TasksRepository实例对象可对数据接口做统一操作。

总结

MVP架构较之以前的MVC架构,在代码模块解耦方面表现更出色,并且解决了Activity责任繁杂的问题。

  • View:只负责显示、绘制、渲染等工作。
  • Model:负责数据操作,如数据库、网络数据、缓存等。
  • Presenter:作为View和Model的桥梁,并负责程序逻辑。

这篇博客是Android架构一系列博客的第一篇,而且是比较标准的MVP架构,看起来有些老调重弹。后续将逐步分析android-architecture中一些比较成熟的架构,并分析与标准MVP架构的优劣。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容