MVPExample
前言
前段时间在看有关android MVP架构方面的资料,在掘金上看到一个不错文章Android MVP 架构必要知识:第一部分
,然后就按照自己理解,写了一个例子(看起来文章很长不怎么想看,其实主要是贴的代码多了都是一个类一个类的(不贴全咯,就觉得少了点什么强迫症!!),,一部分是为了讲述demo有什么功能的展示代码看一下就行,包含了不少不言而喻的细节,真正实现的代码并不多,或者说这个demo的代码并不多,最后会奉上demo地址),各位看官要是觉得有什么不对的或者不合理的地方还请指出来(●’◡’●)。
毕竟还是第一次写博客有点紧张和(/▽\=)啊。好了废话结束进入正题开始码字母 Come with me.
今天我要讲的内容主要以围绕下面的第二张图来说,(这里讨论mvp和mvc具体的差别和使用场景)一般我们在网上看到的有关于mvp相关的介绍,MVP 将一个应用分成了三个基础部分,通过Presenter层
控制view层与model层进行交互,这里我们引进了一个DataManager也就是Model的管理者,
presenter通过DataManager集中控制model层访问数据。例如网络访问,本地数据库访问,例如:
- Model:负责处理应用的数据部分。
- View:负责将带有数据的视图显示在屏幕上。
- Presenter:连接 Model 和 View 的桥梁,它也负责操控 View。
- 对各个不同Model层操作的集中安置和管理,让以后的维护者不必到处找某一个数据操作在什么地方
![]()
这是项目的结构图
从上图可以看出项目结构主要分为 model层接口和实现,DataManager接口和实现,presenter和view的接口集合(GithubContract),和presenter层实现,以及view层activity。图二中虚线所表示的指接口,所以虚线相对应的实线部分就是实现类。
Demo的主要功能是从github上获取项目信息并显示
定义Model,View,Presenter 接口 以及 众多Model数据操作管理者DataManager接口
GithubContract.java这个类定义了presenter层和view层的功能接口。
Presenter 接口作为连接Model和View的中间桥梁,需要将二者连接起来,因此他需要完成以下工作:
- 开始获取数据
- 获取数据之后的成功或者失败的处理
View 接口定义所有需要实现的视图逻辑,在我们的下载任务中,视图逻辑包括
- 显示加载
- 数据获取成功或者失败的回调,展示相应的ui
public interface GithubContract {
/**
* Description:MVP模式的view层
*/
interface GithubView<T> {
/**
* 显示加载中
*/
void showLoading();
/**
* 请求失败的操作
*
* @param tag request flag
*/
void showFailHandle(String tag, String flag);
/**
* 请求成功的操作
*
* @param tag request flag
*/
void showSuccessHandle(T tag, String flag);
}
/**
* Description:MVP模式的Presenter层
*/
interface GithubPresenter {
/**
* 开始请求
*/
<T> void onRequest(String url, Map param, Class<T> tClass, String flag);
/**
* 开始请求
*/
<T> void onRequest(String url, Map param, TypeToken<T> tTypeToken, String flag);
/**
* 请求成功的回调
*
* @param data 数据
*/
<T> void onRequestSuccess(T data, String flag);
/**
* 请求失败的回调
*
* @param msg 信息
*/
void onRequestFail(String msg, String flag);
}
}
GithubModel.java这是model数据层的定义
Model 接口定义所有需要实现的业务逻辑,在我们的demo,业务逻辑只有实现了一个,就是下从github上获取项目信息
public interface GithubModel<T> {
/**
* 回调
*/
interface GithubMCallback<T> {
void callbackSuceed(T t);
void callbackFail(String mag);
}
/**
* 获取数据
*
* @param url
* @param param
* @param tGithubMCallback
*/
void getGithubData(String url, Map param, GithubMCallback<T> tGithubMCallback);
}
DataManager.java这是model层数据集合操作的定义,这里可以定义各种对数据的操作进行集中管理例如网络请求,访问本地数据库等
在这里其实Presenter不直接控制Model层数据操作,而是Presenter通过DataManager间接操作model数据层,只要目的是集中管理Model数据操作,
在DataManager里面定义了从github上获取数据的操作(本demo中实现的功能)以及访问数据库(没有实现).
public interface DataManager {
/**
* 从github上获取数据
*
* @param url
* @param parem
* @param flag
* @param tClass
* @param tGithubPresenter
*/
<T> void getGithubData(String url, Map parem, String flag, Class<T> tClass, GithubPresenterImpl tGithubPresenter);
/**
* 从github上获取数据
*
* @param url
* @param parem
* @param flag
* @param tTypeToken
* @param tGithubPresenter
* @param <T>
*/
<T> void getGithubData(String url, Map parem, String flag, TypeToken<T> tTypeToken, GithubPresenterImpl tGithubPresenter);
/**
* 获取本地数据库中的数据的model
*
* @param <T>
*/
<T> void getDataFromDB(Class<T> tClass);
}
使用过程 接口Model,View,Presenter,DataManager具体实现
Model 具体实现
public class GithubModelImpl<T> implements GithubModel<T> {
// private GithubContract.GithubPresenter mGithubPresenter;//提供者 mvp模式的presenter
private GithubMCallback<T> mGithubCallback;
private URL mURL;
private Class<T> mTClass;
private TypeToken<T> mTypeToken;
public GithubModelImpl(Class<T> pClass) {
this.mTClass = pClass;
}
public GithubModelImpl(TypeToken<T> tTypeToken) {
this.mTypeToken = tTypeToken;
}
@Override
public void getGithubData(String url, Map param, GithubMCallback<T> tGithubMCallback) {
try {
this.mGithubCallback = tGithubMCallback;
mURL = new URL(url);
new GithubTask().execute(mURL);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
private class GithubTask extends AsyncTask<URL, Void, T> {
@Override
protected T doInBackground(URL... params) {
try {
URL url = params[0];
//打开链接
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
//设置请求方式 get post
urlConnection.setRequestMethod("GET");
//设置请求超时
urlConnection.setConnectTimeout(5000);
//设置读数据超时
urlConnection.setReadTimeout(5000);
//获取响应码
int code = urlConnection.getResponseCode();
if (code == 200) {//请求成功
//提取 并 解析数据
//1、提取数据
BufferedReader reader = new BufferedReader(
new InputStreamReader(urlConnection.getInputStream()));
char[] buf = new char[2048];
StringBuilder builder = new StringBuilder();
int size = 0;
while (-1 != (size = reader.read(buf))) {
builder.append(buf, 0, size);
}
reader.close();//关闭流
//2、解析数据
T parsedGSON = null;
if (null != mTClass)
parsedGSON = (T) new Gson().fromJson(builder.toString(), mTClass);
else if (null != mTypeToken)
parsedGSON = (T) new Gson().fromJson(builder.toString(), mTypeToken.getType());
return parsedGSON;
} else {
Log.d("GithubModelImpl", "code:" + code + "==>请求失败信息:" + urlConnection.getResponseMessage());
return null;
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(T t) {
if (t == null) {
mGithubCallback.callbackFail("Fail");
// mGithubPresenter.onRequestFail("Fail", mRequestFlag);
} else {
mGithubCallback.callbackSuceed(t);
// mGithubPresenter.onRequestSuccess(t, mRequestFlag);
}
}
}
}
在MVP模式中,Model的工作就是完成具体的业务操作,网络请求,持久化数据增删改查等任务。同时Model中又不会包含任何View。
这里Model的具体实现很简单,利用异步任务和HttpURLConnection从github上获取开源项目的信息,在onPostExecute方法返回结果,并由model接口中定义的回调接口GithubMCallback回调处理给Presenter使用。
那么Presenter接口又是怎样实现的呢?赶紧来看看
Presenter的实现
public class GithubPresenterImpl implements GithubContract.GithubPresenter {
private GithubContract.GithubView mGithubView;//mvp模式的视图控制
public GithubPresenterImpl(GithubContract.GithubView githubView) {
this.mGithubView = githubView;
}
@Override
public <T> void onRequest(String url, Map param, Class<T> tClass,String flag) {
mGithubView.showLoading();
GithubDataManager.getInstance().getGithubData(url, param, flag, tClass, this);
// this.mGithubModel.getGithubData(url, param, flag);//之前的另一种方法 在presenter层直接去控制model
}
@Override
public <T> void onRequest(String url, Map param, TypeToken<T> tTypeToken, String flag) {
mGithubView.showLoading();
GithubDataManager.getInstance().getGithubData(url, param, flag, tTypeToken, this);
}
@Override
public <T> void onRequestSuccess(T data, String flag) {
mGithubView.showSuccessHandle(data, flag);
}
@Override
public void onRequestFail(String msg, String flag) {
mGithubView.showFailHandle(msg, flag);
}
}
可以看到,我们在GithubPresenterImpl的构造方法中,接收了传入了view,这样Presenter中就可以通过view的回调去通知activity更新UI了;
而在onRequest(…)方法中可以回调显示请求加载框以及开始请求数据,请求数据通过GithubDataManager(单例)去选择操作Model层的某一块功能并把presenter传过去。
这样;在Presenter具体实现中,业务相关的操作由Model去完成(例如download),视图相关的操作由View去完成
(如setView等)。Presenter 作为桥梁的作用就这样体现出来了(其中model层有DataManager集中管理),巧妙的将View和Model的具体实现连接了起来。
DataManager具体实现
public class GithubDataManager implements DataManager {
private static volatile GithubDataManager mInstance;
/**
* 安全的单例模式
*
* @return
*/
public static GithubDataManager getInstance() {
GithubDataManager instance = mInstance;
if (instance == null) {
synchronized (GithubDataManager.class) {
instance = mInstance;
if (null == instance) {
instance = new GithubDataManager();
mInstance = instance;
}
}
}
return instance;
}
/**
* 通过网络 从github上获取数据 的model
*
* @param url
* @param parem
* @param flag
* @param tClass
* @param tGithubPresenter
* @param <T>
* @return
*/
@Override
public <T> void getGithubData(String url, Map parem, final String flag, Class<T> tClass, final GithubPresenterImpl tGithubPresenter) {
new GithubModelImpl<T>(tClass).getGithubData(url, parem, new GithubModel.GithubMCallback<T>() {
@Override
public void callbackSuceed(T t) {
tGithubPresenter.onRequestSuccess(t, flag);
}
@Override
public void callbackFail(String mag) {
tGithubPresenter.onRequestFail(mag, flag);
}
});
}
@Override
public <T> void getGithubData(String url, Map parem, final String flag, TypeToken<T> tTypeToken, final GithubPresenterImpl tGithubPresenter) {
new GithubModelImpl<T>(tTypeToken).getGithubData(url, parem, new GithubModel.GithubMCallback<T>() {
@Override
public void callbackSuceed(T t) {
tGithubPresenter.onRequestSuccess(t, flag);
}
@Override
public void callbackFail(String mag) {
tGithubPresenter.onRequestFail(mag, flag);
}
});
}
/**
* 获取本地数据库中的数据的model
*
* @param tClass
*/
@Override
public <T> void getDataFromDB(Class<T> tClass) {
}
}
GithubDataManager单例实现,因为他只是起到了中间调用者的工作本身没有数据的变化,不需要多实例。
在GithubDataManager中实现了DataManager中的getGithubData(…)和getDataFromDB(…)两个方法分别是访问网络和访问本地数据库(这里只实现一个功能),起到的作用就是对Model层操作的集中安置和管理,让以后的维护者不必到处找某一个数据操作在什么地方。
在getGithubData(。。)方法中实例化GithubModelImpl并传入相应的参数,通过实例去调用这个model操作获取网络数据,再返回结果是通过传入的Presenter对象,将处理结果回调到GithubPresenterImpl,最后在上面GithubPresenterImpl实现中再通过view回调到Activity里面操作UI,最后就是View的实现。
View的实现
public class ModeActivity extends AppCompatActivity implements View.OnClickListener, GithubContract.GithubView {
private static final String REQUEST_TAG_GITHUB = "github";
private static final String REQUEST_TAG_GITHUB_2 = "github_2";
private Button mButMvpGet;
private ProgressBar mLoadingMvp;
private TextView mTextMvpShow;
private Button mButMvpGet2;
private Toolbar mToolbar;
private GithubContract.GithubPresenter mGithubPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);
initView();
mGithubPresenter = new GithubPresenterImpl(this);
}
private void initView() {
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.setTitle(getResources().getString(R.string.app_name));
setSupportActionBar(mToolbar);
mButMvpGet = (Button) findViewById(R.id.but_mvp_get);
mButMvpGet.setOnClickListener(this);
mLoadingMvp = (ProgressBar) findViewById(R.id.loading_mvp);
mLoadingMvp.setOnClickListener(this);
mTextMvpShow = (TextView) findViewById(R.id.text_mvp_show);
mTextMvpShow.setOnClickListener(this);
mButMvpGet2 = (Button) findViewById(R.id.but_mvp_get_2);
mButMvpGet2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.but_mvp_get:
mGithubPresenter
.onRequest("https://api.github.com/repos/CymChad/BaseRecyclerViewAdapterHelper", null, GithubBean.class, REQUEST_TAG_GITHUB);
// mGithubPresenter
// .onRequest("https://api.github.com/repos/CymChad/BaseRecyclerViewAdapterHelper", null, new TypeToken<GithubBean>(){},REQUEST_TAG_GITHUB);
break;
case R.id.but_mvp_get_2:
mGithubPresenter
.onRequest("https://api.github.com/repos/google/guava", null, new TypeToken<GithubBean>() {
}, REQUEST_TAG_GITHUB_2);
break;
}
}
/**
* 请求显示加载中
*/
@Override
public void showLoading() {
mLoadingMvp.setVisibility(0);
}
/**
* 请求失败的操作
*
* @param tag request flag
*/
@Override
public void showFailHandle(String msg, String flag) {
if (REQUEST_TAG_GITHUB.equals(flag)) {
mLoadingMvp.setVisibility(View.GONE);
mTextMvpShow.setText(msg);
} else if (REQUEST_TAG_GITHUB_2.equals(flag)) {
mLoadingMvp.setVisibility(View.GONE);
mTextMvpShow.setText(msg);
}
}
/**
* 请求成功的操作
*
* @param tag request flag
*/
@Override
public void showSuccessHandle(Object data, String flag) {
if (REQUEST_TAG_GITHUB.equals(flag)) {
mLoadingMvp.setVisibility(View.GONE);
mTextMvpShow.setText("name:" + ((GithubBean) data).getName());
} else if (REQUEST_TAG_GITHUB_2.equals(flag)) {
mLoadingMvp.setVisibility(View.GONE);
mTextMvpShow.setText("full_name:" + ((GithubBean) data).getFull_name());
}
}
}
提供了两个按钮,分别获取不同开源项目的信息,异步进行。
在点下按钮执行开始下载任务的时候,View(Activity)中没有具体的实现,只是调用了Presenter中的onRequest方法,而Presenter中的onRequest又会去通过DataManager调用Model的getGithubDatad方法,Model又会在根据具体逻辑(在这里就是Http请求)的状态去调用Presenter中的方法,例如我们在AsyncTask的结果回调方法中,调用mGithubCallback.callbackSuceed(t);时,就会去调用Presenter的具体实现。
/**
* 请求失败的操作
*
* @param tag request flag
*/
@Override
public void showFailHandle(String msg, String flag) {
。。。。。。
}
/**
* 请求成功的操作
*
* @param tag request flag
*/
@Override
public void showSuccessHandle(Object data, String flag) {
。。。。。。
}
而他的内部实现又是操作具体的View,也就是我们在Activity中初始化Presenter中传递的this,也就是当前Activity(View),这样最终回到了Activity中的.
Demo地址:点我、点我、点我