一.在看mvp之前我们首先应该了解android 中的三层架构
1.View层(直接展示给用户的)
2.业务逻辑层(桥接View层和数据访问层的,处理交互、业务逻辑)
3.数据访问层(对数据库操作)
二.传统的mvc
M-Model:对应用状态、业务功能的封装操作,接受Controller的调用完成业务处理,并发起通知更改View
V-View:界面展示,调用model的状态信息并显示
C-Controller:桥接m-v,View收集数据提供给Controller,Controller处理UI逻辑之后更改Model状态
三.mvp
在mvp中是禁止View和Model直接打交道的,需要沟通必须通过Presenter,这样Model/View被分隔开
在mvp中,v代表View抽象出来的的接口
下面一步一步打造自己的mvp
1.抽象一个自己的BasePresenter,上面提到Presenter是M-V沟通的桥梁,分割M-V用的,那么,就必须持有M-V code:
public abstract class BasePresenter<M, T> {
protected M mModel;
protected T mView;
//初始化参数
public void setViewAndModel(T v, M m) {
this.mView = v;
this.mModel = m;
}
/**
* 解除M-V绑定操作
*/
public void unBind(){
if(null != mModel){
mModel = null;
}
if(null != mView){
mView = null;
}
}
2.有必要提供一个基类BaseView,BaseModel
BaseView.class
public interface BaseView {
//显示进度
void showLoading();
//隐藏进度
void hideLoading();
}
BaseModel.class
public interface BaseModel {
}
这里M-V-P就有骨架子了,下面需要把他们结合使用起来,需要构建一个约定,把三者结合
DemoContract.class
public interface DemoContract {
interface DemoView extends BaseView {
void refreshing();
}
interface DemoModel extends BaseModel {
void setUserName(String name);
void setUserSex(int sex);
}
abstract class Presenter extends BasePresenter<DemoModel, DemoView> {
public abstract void login(String username, String password, String passcode);
}
这里会发现Presenter里面传入的DemoModel,DemoView, DemoView就是我们刷新页面的接口,重点是DemoModel,这里的Model不是实体类(例如:服务器返回数据),而是我根据页面抽出来的一个对象,只用户页面展示
DemoModel.class
public class DemoModel implements DemoContract.DemoModel {
private String userName;
private int userSex;
@Override
public void setUserName(String name) {
this.userName = name;
}
@Override
public void setUserSex(int sex) {
this.userSex = sex;
}
public String getUserName() {
return userName;
}
public int getUserSex() {
return userSex;
}
}
定义DemoPresenter去继承约定里面的Presenter,用来处理业务逻辑
DemoPresenter.class
public class DemoPVPresenter extends DemoContract.Presenter{
@Override
public void login(String username, String password, String passcode) {
mModel.setUserName(username);
mModel.setUserSex(1);
//调用视图刷新刷新
mView.refreshing();
}
}
到这里,我的mvp就构建完了,最重要的就是V层的使用了,首先定义一个DemoBaseActivity,需要我们传入P和M,作为约束
DemoBaseActivity.class
public abstract class DemoBaseActivity<T extends BasePresenter, E extends BaseModel> extends Activity {
//继承类返回自己的xml文件名字,用于购建页面
public abstract int initLayoutID();
public abstract void handleOnCreated(Bundle savedInstanceState);
//业务逻辑处理
protected T mPresenter;
//业务model
protected E mModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
int layoutResID = initLayoutID();
setContentView(layoutResID);
handleOnCreated(savedInstanceState);
mPresenter = MVPUtil.getT(this, 0);
mModel = MVPUtil.getT(this, 1);
if (this instanceof BaseView) {
mPresenter.setViewAndModel(this, mModel);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//解除绑定
if (null != mPresenter) {
mPresenter.unBind();
mPresenter = null;
}
}
}
BaseFragment.class
/**
* mvp fragment基类
*
* @param <P>
* @param <M>
*/
public abstract class BaseFragment<P extends BasePresenter, M extends BaseModel> extends Fragment {
protected Activity mActivity;
protected Context mContext;
protected View mRootView;
//是否已经创建过
protected boolean mIsOnCreateView;
protected P mFragmentPresenter;
protected M mFragmentModel;
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.mContext = context;
this.mActivity = ((Activity) context);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView == null) {
this.mIsOnCreateView = true;
mRootView = getRootView();
getIntentArguments();
initView();
} else {
this.mIsOnCreateView = false;
}
//如果该fragment 已经被加入过容器,从容器中移除
ViewGroup localViewGroup = (ViewGroup) mRootView.getParent();
if (localViewGroup != null) {
localViewGroup.removeView(mRootView);
}
//赋值参数
mFragmentPresenter = MVPUtil.getT(this, 0);
mFragmentModel = MVPUtil.getT(this, 1);
if (this instanceof BaseView) {
mFragmentPresenter.initParam(mFragmentModel, this);
}
return mRootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//如果已经创建直接填充或者刷新数据
if (mIsOnCreateView) {
setView();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
try {
//解决fragment找不到activity的问题
Field localField = Fragment.class.getDeclaredField("mChildFragmentManager");
localField.setAccessible(true);
localField.set(this, null);
} catch (NoSuchFieldException localNoSuchFieldException) {
throw new RuntimeException(localNoSuchFieldException);
} catch (IllegalAccessException localIllegalAccessException) {
throw new RuntimeException(localIllegalAccessException);
}
}
@Override
public void onDestroy() {
super.onDestroy();
//解除绑定
if (null != mFragmentPresenter) {
mFragmentPresenter.unBind();
mFragmentPresenter = null;
}
}
/**
* 获取根布局
*/
protected abstract View getRootView();
/**
* 获取页面参数
*/
protected abstract void getIntentArguments();
/*****
* 初始化控件
*/
protected abstract void initView();
/**
* 设置view数据
*/
protected abstract void setView();
}
里面有一个泛型获取工具
MVPUtil.class
public class MVPUtil {
public static <T> T getT(Object o, int i) {
try {
return ((Class<T>) ((ParameterizedType) (o.getClass()
.getGenericSuperclass())).getActualTypeArguments()[i])
.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassCastException e) {
e.printStackTrace();
}
return null;
}
}
最后,就是使用了
DemoActivity.class
public class DemoActivity extends DemoBaseActivity<DemoPVPresenter,DemoModel> implements DemoContract.DemoView {
TextView demoTv;
Button demoBtn;
@Override
public void showLoading() {
}
@Override
public void hideLoading() {
}
@Override
public int initLayoutID() {
return R.layout.activity_demo_layout;
}
@Override
public void handleOnCreated(Bundle savedInstanceState) {
demoTv = findViewById(R.id.demo_tv);
demoBtn = findViewById(R.id.demo_btn);
demoBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mPresenter.login("jiangHua","123456","100");
}
});
}
@Override
public void refreshing() {
demoTv.setText(new StringBuilder().append(mModel.getUserName()).append(mModel.getUserSex()).toString());
}
这个是我自己弄的,有优化整改意见的,欢迎大家指出
github地址:https://github.com/otherface/Android-mvp