1. 关键点
重要的是要牢记以下几个要点:
- model中:封装数据存取操作(包括操作数据库或通过网络获取数据)和实体模型;
- view:指Activity、Fragment或某一具体控件。view中持有presenter的引用;
- presenter:沟通model和view的桥梁。presenter中持有view和model的引用;
- 遵循面向接口编程的准则
2. 示例代码
打开一个有列表的Activity,请求数据然后刷新界面。
目录结构
写代码流程
- 从View开始写起,当View中需要操作数据时,调用presenter的相关方法。
/**
*
* author : 杨丽金
* time : 2017/12/27
* desc : 打开一个有列表的Activity界面,请求数据然后刷新界面(MVP写法)
* version: 1.0
*
*/
public class NewsActivity extends AppCompatActivity implements NewsView {
private ListView mLvMvp;
private ProgressBar mPbProgress;
// View中持有Presenter层的引用
private NewsPresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);
initView();
initData();
}
private void initData() {
initNewsList();
}
// 初始化列表
private void initNewsList() {
// 请求数据
presenter.requestNewsData();
}
private void initView() {
mLvMvp = (ListView) findViewById(R.id.mvp_listview);
mPbProgress = (ProgressBar) findViewById(R.id.mvp_loading);
presenter = new NewsPresenterImpl(this);
}
}
- presenter再调用model中的相关方法;
/**
*
* author : 杨丽金
* time : 2017/12/27
* desc : presenter是model和view之间的桥梁,它不做具体操作,view划分model才是操作的具体执行者
* version: 1.0
*
*/
public class NewsPresenterImpl implements NewsPresenter {
// presenter中持有View引用
private NewsView newsView;
// presenter中持有model引用
private NewsModel newsModel;
// 消息处理者
private Handler handler;
public NewsPresenterImpl(NewsView newsView) {
this.newsView = newsView;
// 在构造函数中初始化变量
newsModel = new NewsModelImpl();
// 表示该handler要用主线程的Looper,将消息发送到主线程的MessageQueue中处理消息
handler = new Handler(Looper.getMainLooper());
}
// 请求新闻数据
@Override
public void requestNewsData() {
// 当数据请求完成后的回调方法
newsView.showProgress();
newsModel.getNewsData(new OnReqNewsDataFinishedListener() {
@Override
public void onSuccess(final ArrayList data) {
/*
onSucces()方法是处理数据的子线程处理完数据后回调的方法,所以onSuccess()是在子线程运行的,
故在该房中更新UI时要用handler
*/
handler.post(new Runnable() {
@Override
public void run() {
if (newsView != null) {
newsView.hideProgress();
newsView.setListItems(data);
}
}
});
}
@Override
public void onFailed() {
handler.post(new Runnable() {
@Override
public void run() {
if (newsView != null) {
newsView.hideProgress();
newsView.showMessage("请求错误");
}
}
});
}
});
}
}
- model加载数据完成后调用presenter中的回调方法返回数据加载完成情况
/**
*
* author : 杨丽金
* time : 2017/12/27
* desc :
* version: 1.0
*
*/
public class NewsModelImpl implements NewsModel {
@Override
public void getNewsData(final OnReqNewsDataFinishedListener listener) {
// 开启一个线程,模拟获取数据过程
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
ArrayList data = new ArrayList();
for(int i=0;i<8;i++) {
data.add("item" + i);
}
int i=1/0;
if (listener != null) {
listener.onSuccess(data);
}
} catch (InterruptedException e) {
e.printStackTrace();
listener.onFailed();
}catch (Exception ex){
ex.printStackTrace();
listener.onFailed();
}
}
}).start();
}
}
- presenter知道数据加载情况(加载成功或失败或其他意外情况)后,调用view的接口指使view完成界面跳转等操作
/**
*
* author : 杨丽金
* time : 2017/12/27
* desc : presenter是model和view之间的桥梁,它不做具体操作,view划分model才是操作的具体执行者
* version: 1.0
*
*/
public class NewsPresenterImpl implements NewsPresenter {
// presenter中持有View引用
private NewsView newsView;
// presenter中持有model引用
private NewsModel newsModel;
// 消息处理者
private Handler handler;
public NewsPresenterImpl(NewsView newsView) {
this.newsView = newsView;
// 在构造函数中初始化变量
newsModel = new NewsModelImpl();
// 表示该handler要用主线程的Looper,将消息发送到主线程的MessageQueue中处理消息
handler = new Handler(Looper.getMainLooper());
}
// 请求新闻数据
@Override
public void requestNewsData() {
// 当数据请求完成后的回调方法
newsView.showProgress();
newsModel.getNewsData(new OnReqNewsDataFinishedListener() {
@Override
public void onSuccess(final ArrayList data) {
/*
onSucces()方法是处理数据的子线程处理完数据后回调的方法,所以onSuccess()是在子线程运行的,
故在该房中更新UI时要用handler
*/
handler.post(new Runnable() {
@Override
public void run() {
if (newsView != null) {
newsView.hideProgress();
newsView.setListItems(data);
}
}
});
}
@Override
public void onFailed() {
handler.post(new Runnable() {
@Override
public void run() {
if (newsView != null) {
newsView.hideProgress();
newsView.showMessage("请求错误");
}
}
});
}
});
}
}
注意
- 在model中开启了子线程进行数据处理,处理完成后回调presenter中的onSuccess()和onFailed()方法,故onSuccess()和onFailed()方法也是在子线程中执行,所以这两个方法中更新UI的操作要用Handler异步消息处理机制
/**
*
* author : 杨丽金
* time : 2017/12/27
* desc : presenter是model和view之间的桥梁,它不做具体操作,view划分model才是操作的具体执行者
* version: 1.0
*
*/
public class NewsPresenterImpl implements NewsPresenter {
// 消息处理者
private Handler handler;
public NewsPresenterImpl(NewsView newsView) {
// 表示该handler要用主线程的Looper,将消息发送到主线程的MessageQueue中处理消息
handler = new Handler(Looper.getMainLooper());
}
// 请求新闻数据
@Override
public void requestNewsData() {
// 当数据请求完成后的回调方法
newsView.showProgress();
newsModel.getNewsData(new OnReqNewsDataFinishedListener() {
@Override
public void onSuccess(final ArrayList data) {
/*
onSucces()方法是处理数据的子线程处理完数据后回调的方法,所以onSuccess()是在子线程运行的,
故在该房中更新UI时要用handler
*/
handler.post(new Runnable() {
@Override
public void run() {
if (newsView != null) {
newsView.hideProgress();
newsView.setListItems(data);
}
}
});
}
@Override
public void onFailed() {
handler.post(new Runnable() {
@Override
public void run() {
if (newsView != null) {
newsView.hideProgress();
newsView.showMessage("请求错误");
}
}
});
}
});
}
}
代码地址
有列表的Activity Demo
参考文献
Android MVP架构的自述