Android mvp 官方实例解析

一 官方文档说明

概要

这个例子是很多实例变化的基础(后续还有一些实例 ),是mvp模式的简单实现,它采用依赖注入的方式为用户提供本地和远程数据源的存储库,用回调的方式进行异步任务的处理。

mvp 概要图片

注意:在mvp结构中,view 被重载

  • android.view.VIew 类将会被当作 “android view”
  • view 接收来自解析器(presenter)的指令,被简称为 “view”

Fragments

使用 fragments的两个原因

  • 把Activity和fragment 分离 是非常适合实现mvp的,Activity 完全控制和连接view 和 presenters.
  • 使用fragment 有利于平板或者多分辨率

概念

四个功能

  • Tasks
  • TaskDetails
  • AddEditTask
  • Statistics

每个功能都有

  • 在view 和presenter之间都定义好了协议

  • Activity负责创建fragment和presenters

  • fragment 实现view 的接口

  • presenter 实现 presenter接口

    一般来说,业务逻辑执行在presenter中,view 用来执行android 的ui工作
    view 大多数情况没有业务逻辑,它主要将presenter的命令转为ui操作,并监听用户的操作传递给presenter.
    规范接口 用于定义连接 view和presenters。

程序运行

如图所示 先就增加一个任务的操作进行代码分享

![Screenshot_2016-09-13-14-24-04.png](http://upload-images.jianshu.io/upload_images/1658181-63c8ee941b591d13.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

代码分析

AddEditTaskActiviy (增加任务的activity)

@Override
protected void onCreate(Bundle savedInstanceState) {    
  super.onCreate(savedInstanceState);    
setContentView(R.layout.addtask_act);  、
 // Set up the toolbar.  设置标题
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);   
setSupportActionBar(toolbar);   
ActionBar actionBar = getSupportActionBar(); 
actionBar.setDisplayHomeAsUpEnabled(true);  
actionBar.setDisplayShowHomeEnabled(true);  
// 1.创建fragment  
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);
Bundle bundle = new Bundle();   
bundle.putString(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID, taskId);       
addEditTaskFragment.setArguments(bundle); 
   } else {            actionBar.setTitle(R.string.add_task);        }   
 ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),      
  addEditTaskFragment, R.id.contentFrame);    }  
 // Create the presenter    2.创建 presenter 
new   AddEditTaskPresenter( taskId,  Injection.provideTasksRepository(getApplicationContext()),          
addEditTaskFragment);}

结合文档和代码,再activity的oncreate方法中,主要两个操作

  • 创建fragment
  • 实例化 presenter

AddEditTaskFragment (View) 代码

@Override
public 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() { 
   @Override
    public void onClick(View v) {
        mPresenter.saveTask(mTitle.getText().toString(), mDescription.getText().toString()); 
   }
});
}

在 onActivityCreate中 实现了完成增加任务的按钮,把用户的操作传递给presenter

AddEditTaskFragment 实现了AddEditTaskContract.view接口

AddEditTaskContract 代码

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 的接口,供view去实现里面的方法,定义了presenter接口供对应的presenter去实现。

BaseView 基view 因为所有的view都要关联一个presenter
<pre><code> public interface BaseView<T> {
void setPresenter(T presenter);
}
</pre></code>
看完View层 ,再看P层

AddEditTaskPresenter (P 层代码)

实现了 AddEditTaskContract.Presenter接口
构造方法

//三个参数  任务 id,数据源,关联的view
public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource     
tasksRepository,   
@NonNull AddEditTaskContract.View addTaskView) {
 mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository);
mAddTaskView = checkNotNull(addTaskView);
//view 设置 presenter
mAddTaskView.setPresenter(this);

}

接下来看AddEditTaskFragment 中完成按钮的点击事件
<pre><code> mPresenter.saveTask(mTitle.getText().toString(), mDescription.getText().toString());
</pre></code>
也就是present的saveTask方法

@Override 
public void saveTask(String title, String description) {
 if (isNewTask()) {
    createTask(title, description);
} else {
    updateTask(title, description);
}}

直接看 createTask方法

private void createTask(String title, String description) {
//新建一个任务
Task newTask = new Task(title, description);
if (newTask.isEmpty()) {
    mAddTaskView.showEmptyTaskError();
} else {
    //保存数据
    mTasksRepository.saveTask(newTask);
   //通知view 层展示新的数据 (前面文档:view层通过接收present的指令)
    mAddTaskView.showTasksList();
}}

AddEditTaskPresenter 还实现了 TasksDataSource.GetTaskCallback

TasksDataSource(M 层)

数据层的两个方法

@Override
public void onTaskLoaded(Task task) {
// The view may not be able to handle UI updates anymore
if (mAddTaskView.isActive()) {
    mAddTaskView.setTitle(task.getTitle());            
    mAddTaskView.setDescription(task.getDescription());
  }}
@Override
public void onDataNotAvailable() {
// The view may not be able to handle UI updates anymore
if (mAddTaskView.isActive()) {
    mAddTaskView.showEmptyTaskError();
}}

获取数据的接口

/**
 * Main entry point for accessing tasks data.
 * <p> * For simplicity, only getTasks() and getTask() have callbacks. Consider adding callbacks      to other 
 * methods to inform the user of network/database errors or successful operations.
 * For example, when a new task is created, it's synchronously stored in cache but usually every 
 * operation on database or network should be executed in a different thread. 
 */

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);
void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
void saveTask(@NonNull Task task);
void completeTask(@NonNull Task task);
void completeTask(@NonNull String taskId);
void activateTask(@NonNull Task task);
void activateTask(@NonNull String taskId);
void clearCompletedTasks();
void refreshTasks();
void deleteAllTasks();
void deleteTask(@NonNull String taskId);}

MVP的好处

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

推荐阅读更多精彩内容