接触Android开发已经1年多了,记得刚开始写Android时不知道所谓什么框架,上来就写,大多数代码写在一个文件里,导致代码结构不清晰,显得非常混乱。接触的多了,慢慢会学到很多东西,上个App在开发时就学习使用了MVP架构,现在回顾并将其记载博客与大家分享,同时也是自己再深入学习MVP框架相关知识。
这里的MVP,不是全场最佳选手。Android里面的MVP指的是一种框架,MVP分别指Model(实体层)、View(视图层)、Presenter(中间层),简单来说,就是将代码分层来写,以使代码结构清晰, 有效的降低View的复杂性 ,同时又带来了良好的可扩展性,可测试性,保证了系统的整洁性和灵活性。
关于MVP的具体描述,网络上有很多文章有叙述,如果对MVP不了解可以去看看一些基础概述文章。我这里就不再赘述了,直接分享我对MVP框架的使用和优化。 希望对学习中的朋友有一定的用处。为了方便叙述,我以自己写的一个实例demo为例说明。此demo简单实现了对一个API的网络请求,并显示结果。
首先看一下代码目录结构:
每一层都用每一层的类与接口的形式,以实现在各层之间通过接口相互联系。为了更好的实现,写了Activity基类和Presenter基类,均为泛型抽象类。
BaseActivity类,他是一个抽象泛型Activity基类,所有实现的Activity都通过继承它实现(如果对泛型缺乏了解的朋友可以看前面我分享的两篇关于泛型的文章):
package fun.qianxiao.mvpdemo.base;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
/**
* Create by QianXiao
* On 2020/4/15
*/
public abstract class BaseActivity<T extends BasePresenter<?,?>>
extends AppCompatActivity{
public Context context;
/**
* 对应的Presenter层的类对象
*/
public T mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//使用抽象方法在子类中实现来绑定对应layout页面
setContentView(getLayoutID());
context = this;
//使用抽象方法在子类中实现来初始化Presenter层的类对象
mPresenter = initPresenter();
initView();
initListener();
initData();
}
protected abstract int getLayoutID();
protected abstract T initPresenter();
protected abstract void initView();
protected abstract void initListener();
protected abstract void initData();
/**
* 绑定控件
* @param id
* @param <E>
* @return
*/
public <E> E f(int id){
return (E)findViewById(id);
}
//抽象父类里也可以写一些公共方法
public void Toast(String s){
Toast.makeText(context,s,Toast.LENGTH_SHORT).show();
}
}
一个泛型参数T,T类型为BasePresenter类的子类,BasePresenter 类 从类的命名也可以看出也是一个抽象父类 ,为所有Presenter类的父类。 此外使用了2个抽象方法分别来获取并绑定layout id和初始化Presenter层的类对象。
BasePresenter 代码如下:
package fun.qianxiao.mvpdemo.base;
import android.content.Context;
/**
* Create by QianXiao
* On 2020/4/15
*/
public abstract class BasePresenter<T,V> {
public Context context;
/**
* View层接口对象
*/
public T mView;
/**
* Model层接口对象
*/
public V mModel;
public BasePresenter(Context context) {
this.context = context;
}
/**
* 绑定view层接口
* @param mView
*/
public void attach(T mView){
this.mView = mView;
}
}
该类有两个泛型参数T、V,类中含一个T类型的mView变量,它是一个view层的接口对象,和一个V类型的mModel变量,为对应的model层的接口对象。其含有一个attch方法用来设置 view层接口,如此我们实现的所有Presenter类都继承这个抽象父类,即可优雅地将view层即可和Presenter层联系在一起。
说 MainActivity 之前先说下MainPresenter的实现,它继承自BasePresenter类,泛型参数分别传view层和model层的接口,代码如下:
package fun.qianxiao.mvpdemo.main.presenter;
import android.content.Context;
import fun.qianxiao.mvpdemo.base.BasePresenter;
import fun.qianxiao.mvpdemo.main.model.IMainModel;
import fun.qianxiao.mvpdemo.main.model.MainModel;
import fun.qianxiao.mvpdemo.main.view.IMainView;
/**
* Create by QianXiao
* On 2020/4/15
*/
public class MainPresenter
extends BasePresenter<IMainView, IMainModel>
implements IMainPresenter, IMainModel.RefreshCallback {
public MainPresenter(Context context) {
super(context);
mModel = new MainModel(context);
}
@Override
public void OnRefresh() {
mView.OpenLoadingDialog();
//将实现的回调接口(IMainModel.RefreshCallback)实现并传入
mModel.OnRefreshData(this);
}
@Override
public void RefreshSuccess(String data) {
mView.CloseRefresh();
mView.CloseLoadingDialog();
mView.RefreshDate(data);
}
@Override
public void RefreshError(String e) {
mView.CloseRefresh();
mView.CloseLoadingDialog();
mView.Toast(e);
}
}
在构造函数中将父类的mModel 初始化为Model层类对象(接口指向对象)。实现了IMainPresenter 接口方法OnRefresh(),代表刷新操作,实现了IMainModel接口中子接口RefreshCallback中的方法RefreshSuccess(String)和RefreshError(String),用于 OnRefresh() 中向model层传递回调。
MainModel类中进行网络请求并将结果返回给回调:
package fun.qianxiao.mvpdemo.main.model;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Date;
import fun.qianxiao.mvpdemo.tool.EncryptUtils;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* Create by QianXiao
* On 2020/4/15
*/
public class MainModel implements IMainModel {
Context context;
RefreshCallback callback;
Handler handler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
callback.RefreshError((String) msg.obj);
break;
case 1:
callback.RefreshSuccess((String) msg.obj);
break;
}
}
};
public MainModel(Context context) {
this.context = context;
}
@Override
public void OnRefreshData(final RefreshCallback callback) {
this.callback = callback;
OkHttpClient okHttpClient = new OkHttpClient
.Builder()
.build();
final String url = "http://api.qianxiao.fun/prattleoflover/get.php";
String nowtimestamp = String.valueOf(new Date().getTime()/1000);
RequestBody body = new FormBody.Builder()
.add("timestamp",nowtimestamp)
.add("parsign",EncryptUtils.encryptSHA1ToString("timestamp="+nowtimestamp+"prattleoflover"))
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Message message = new Message();
message.what = 0;
message.obj = e.getMessage();
handler.sendMessage(message);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String responsestr = response.body().string();
try{
JSONObject jsonObject = new JSONObject(responsestr);
Message message = new Message();
message.obj = jsonObject.getString("msg");
if(jsonObject.getInt("code")==1){
message.what = 1;
handler.sendMessage(message);
}else{
message.what = 0;
handler.sendMessage(message);
}
}catch (JSONException e) {
e.printStackTrace();
}
}
});
}
}
然后是MainActivity类(view层)的实现,继承自BaseActivity:
package fun.qianxiao.mvpdemo.main; import android.app.ProgressDialog; import android.support.v4.widget.SwipeRefreshLayout; import android.widget.TextView; import fun.qianxiao.mvpdemo.R; import fun.qianxiao.mvpdemo.base.BaseActivity; import fun.qianxiao.mvpdemo.main.presenter.MainPresenter; import fun.qianxiao.mvpdemo.main.view.IMainView; public class MainActivity extends BaseActivity<MainPresenter> implements IMainView { SwipeRefreshLayout srl_main; TextView tv_info; ProgressDialog progressDialog; @Override protected int getLayoutID() { return R.layout.activity_main; } @Override protected MainPresenter initPresenter() { MainPresenter presenter = new MainPresenter(context); presenter.attach(this); return presenter; } @Override protected void initView() { srl_main = f(R.id.srl_main); tv_info = f(R.id.tv_info); } @Override protected void initListener() { srl_main.setOnRefreshListener(() -> mPresenter.OnRefresh()); } @Override protected void initData() { progressDialog = new ProgressDialog(context); progressDialog.setMessage("加载中"); mPresenter.OnRefresh(); } @Override public void OpenLoadingDialog() { if(!progressDialog.isShowing()) { progressDialog.show(); } } @Override public void CloseLoadingDialog() { if(progressDialog.isShowing()){ progressDialog.dismiss(); } } @Override public void CloseRefresh() { if(srl_main.isRefreshing()){ srl_main.setRefreshing(false); } } @Override public void RefreshDate(String data) { tv_info.setText(data); } @Override public void Toast(String s) { super.Toast(s); } }
首先实现其父类的抽象方法getLayoutID(int)用于绑定layout页面。实现父类的抽象方法initPresenter()来初始化Presenter层对象。initView()、initListener()、initData()也均为父类的抽象方法,用来初始化视图绑定、初始化监听事件和初始化数据等。个人习惯这样将代码分离,以使代码结构更清晰,如果不想用,也可以重写onCreate(Bundle)方法。然后是实现的IMainView接口中的5个方法。
总体的调用逻辑为,view层通过mPresenter的OnRefresh()方法来调用刷新操作,presenter层又调用model层的OnRefreshData(RefreshCallback)方法来具体进行网络请求操作,并将结果返回给回调(即presenter层实现的IMainModel.RefreshCallback接口)。
总结一下,mvp框架主要就是如此以实现代码的分离,又利用接口联系起来。本文主要对mvp框架的使用利用泛型、抽象类、接口的知识进行了一定的优化。当然,如果你有需要,也可以再实现view层接口的父接口,以及model类的抽象父类等。
最后附上源码:https://github.com/qx0657/MVPDemo
如果你对本文有更好的建议,欢迎评论交流。如有什么不足欢迎指点评论。
安卓梦雪QQ2487686673
请登录后发表评论
注册
社交帐号登录