人之因此能,是相信能。android
说到MVP,你们应该都不陌生了,因为其高度解等等优势,愈来愈多的项目使用这个设计模式。然而,优势虽在,缺点也很多,其中一个就是类多了不少,并且V与P直接要项目通讯,那么P就得持有V得实例,但若是活动挂掉了,若是没有对V进行释放,还有致使内存溢出得问题,并且,那么多的接口函数,看获得人眼花缭乱,也使得不少人在使用这个模式的时候望而尚步。数据库
回归正题,最近在进行代码重构,决定采用MVP模式进行开发。若是咱们不进行封装,单纯地简单使用MVP来开发,这要就会出现如上的问题,接口和类多并且重复。和别人协同开发也存在问题。那么对MVP模式进行封装就显得很重要了。固然,一千我的中有一千个哈姆雷特,这里提供一下个人思路,供你们参考。设计模式
MVP模式至关于在MVC模式中又加了一个Presenter用于处理模型和逻辑,将View和Model彻底独立开,在android开发中的体现就是activity仅用于显示界面和交互,activity不参与模型结构和逻辑。bash
使用MVP模式会使得代码多出一些接口可是使得代码逻辑更加清晰,尤为是在处理复杂界面和逻辑时,咱们能够对同一个activity将每个业务都抽离成一个Presenter,这样代码既清晰逻辑明确又方便咱们扩展。固然若是咱们的业务逻辑自己就比较简单的话使用MVP模式就显得,没那么必要。因此咱们不须要为了用它而用它,具体的仍是要要业务须要。微信
其实,简而言之:view就是UI,model就是数据处理,而persenter则是他们的纽带。网络
MVP模式仍是存在一些不足之处的,最大的不足就是类的快速增多,但相对于MVC的臃肿、MVP的高度解耦来讲,类的增多可能就洒洒水啦~~~框架
上图介绍:ide
Contract:契约类,一个功能模块中View接口、Model接口和请求数据回调统一在对应模块的Contract中定义,便于管理。函数
ViewInterface: view层接口,定义了view中的UI操做工具
ModelInterface: model层接口,定义了model负责的数据操做方法,如请求接口,操做数据库等
CallbackInterface: model层操做数据完成后的回调
BasePersenter: Persenter父类,主要是对相关view的获取,销毁等操做
View: view层实现类,主要就是Activity或Fragment,负责UI展现和事件响应
Model: model层实现类,就是依据业务,请求对应接口或数据库,并将结果返给回调CallBack
Persenter: persenter层类,负责业务逻辑处理,view将响应传给persenter,persenter负责调用model,并将结果返回给view供其展现
/**
* Description: Presenter的根父类
* Created by jia on 2016/10/27.
* 人之因此能,是相信能
*/
public abstract class BasePresenter<T> {
//View接口类型的软引用
protected Reference<T> mViewRef;
public void attachView(T view) {
//创建关系
mViewRef = new SoftReference<T>(view);
}
protected T getView() {
return mViewRef.get();
}
public boolean isViewAttached() {
return mViewRef != null && mViewRef.get() != null;
}
public void detachView() {
if (mViewRef != null) {
mViewRef.clear();
}
}
}
复制代码
先来观察下这个base类:
先设置一泛型T,T为与presenter相关的view。BasePresenter中持有一个view的软引用。
在关联方法中将view对象传入,并存入软引用中,建立获取、取消关联和判断方法。
至于使用软引用,是为了防止所持的view都销毁了,但presenter一直持有,致使内存泄漏。
view的封装,主要是BaseActivity和BaseFragment的封装。
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends FragmentActivity {
public String TAG = getClass().getSimpleName() + "";
protected T mPresenter;
public Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
initActivityView(savedInstanceState);
mContext = BaseActivity.this;
//建立presenter
mPresenter = createPresenter();
// presenter与view绑定
if (null != mPresenter) {
mPresenter.attachView((V) this);
}
findViewById();
getData();
}
/**
* 关于Activity的界面填充的抽象方法,须要子类必须实现
*/
protected abstract void initActivityView(Bundle savedInstanceState);
/**
* 加载页面元素
*/
protected abstract void findViewById();
/**
* 建立Presenter 对象
*
* @return
*/
protected abstract T createPresenter();
protected abstract void getData();
@Override
protected void onDestroy() {
super.onDestroy();
...
if (null != mPresenter) {
mPresenter.detachView();
}
}
}
复制代码
BaseActivity设置两个泛型——V和P,明显地,分别表明对应的View和Presenter。
其持有一个BasePresenter,在onCreated方法中,使用createPresenter方法返回对应的BasePresenter的子类,咱们就可使用了。
这里注意一下view和presenter的处理:在onCreated中建立Presenter对象,但其内部的view软引用仍是空;在onResume中关联view,此时presenter已经持有view的软引用;固然,还须要在onDestroy中取消关联。
至于其余的封装就再也不介绍了,相信你们确定还有更优的封装方法。
public abstract class BaseFragment<V, T extends BasePresenter<V>> extends Fragment {
public String TAG = getClass().getSimpleName() + "";
private static final String STATE_SAVE_IS_HIDDEN = "STATE_SAVE_IS_HIDDEN";
protected T mPresenter;
//定义一个View用来保存Fragment建立的时候使用打气筒工具进行的布局获取对象的存储
protected View view;
/**
* 当Fragment进行建立的时候执行的方法
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();//建立presenter
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden());
}
/**
* 这个方法是关于Fragment完成建立的过程当中,进行界面填充的方法,该方法返回的是一个view对象
* 在这个对象中封装的就是Fragment对应的布局
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = initFragmentView(inflater);
return view;
}
/**
* 这个方法当onCreateView方法中的view建立完成以后,执行
* 在inflate完成view的建立以后,能够将对应view中的各个控件进行查找findViewById
*/
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
initFragmentChildView(view);
}
/**
* 这个方法是在Fragment完成建立操做以后,进行数据填充操做的时候执行的方法
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initFragmentData(savedInstanceState);
}
/**
* 完成打气筒操做
*/
protected abstract View initFragmentView(LayoutInflater inflater);
/**
* 进行findViewById的操做
*
* @param view 打气筒生成的View对象
*/
protected abstract void initFragmentChildView(View view);
/**
* 网络数据填充的操做
*
* @param savedInstanceState
*/
protected abstract void initFragmentData(Bundle savedInstanceState);
/**
* 建立Presenter对象
*/
protected abstract T createPresenter();
@Override
public void onResume() {
super.onResume();
if (null != mPresenter) {
mPresenter.attachView((V) this);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (null != mPresenter) {
mPresenter.detachView();
}
}
}
复制代码
BaseFragment与BaseA相似就再也不累赘。
契约,顾名思义,规范定义,定义功能和模板。
在契约类中定义View的接口,Model的接口。由于Model将数据返给Presenter是使用回调方式,因此还须要再契约类中定义对应的回调。
具体看示例吧。
这里咱们以登陆功能模块为例:
/**
* Description:
* Created by jia on 2017/12/20.
* 人之因此能,是相信能
*/
public class LoginContract {
public interface LoginView{
void onCheckFormatSuccess();
void onCheckFormatFail(String info);
void onLoginSuccess(Login login);
void onLoginFail(String errorInfo);
}
public interface LoginModel{
void login(String name,String password,LoginCallBack callBack);
}
public interface LoginCallBack{
void onSuccess(Login login);
void onFail(String errorInfo);
}
}
复制代码
这里定义了登陆页面的view接口、model接口和对应的回调。
在view中,只定义与UI展现的相关方法,如检查帐号密码格式成功(失败)、登陆成功(失败)等。
model负责数据请求,因此在接口中只定义了登陆的方法。
回调定义了登陆成功仍是失败的方法。
/**
* Description: 登陆 Model实现类
* Created by jia on 2017/12/20.
* 人之因此能,是相信能
*/
public class LoginModelImpl implements LoginContract.LoginModel {
/**
* 登陆方法
* @param name
* @param password
* @param callBack
*/
@Override
public void login(String name, String password, final LoginContract.LoginCallBack callBack) {
LoginNetUtils.getInstance().login(name, password, new BaseSubscriber<Login>() {
@Override
public void onSuccess(Login login) {
callBack.onSuccess(login);
}
@Override
public void onFail(String info) {
callBack.onFail(info);
}
});
}
}
复制代码
建立Model实现类,重写其登陆方法既可,将登陆接口交给回调。
/**
* Description: 登陆主持类
* Created by jia on 2017/12/20.
* 人之因此能,是相信能
*/
public class LoginPresenter extends BasePresenter<LoginContract.LoginView> {
private LoginModelImpl model;
public LoginPresenter() {
model = new LoginModelImpl();
}
/**
* 检查格式
*
* @param name
* @param password
*/
public void checkFormat(String name, String password) {
if (TextUtils.isEmpty(name)) {
getView().onCheckFormatFail("请输入用户名");
} else if (TextUtils.isEmpty(password)) {
getView().onCheckFormatFail("请输入密码");
} else if (password.length() < 6 || password.length() > 18) {
getView().onCheckFormatFail("密码格式不正确");
} else {
getView().onCheckFormatSuccess();
login(name, password);
}
}
/**
* 登陆
*
* @param name
* @param password
*/
public void login(String name, String password) {
model.login(name, password, new LoginContract.LoginCallBack() {
@Override
public void onSuccess(Login login) {
getView().onLoginSuccess(login);
}
@Override
public void onFail(String errorInfo) {
getView().onLoginFail(errorInfo);
}
});
}
}
复制代码
LoginPresenter集成BasePresenter,传入LoginView最为泛型T。
内部持有Model实现类对象。
建立两个方法,一个是检查格式,一个是登陆。两个方法就是业务的处理。
如登陆方法,登陆返回后,在回调中获得数据,也能够再进行一些逻辑判断,将结果交给view的对应的方法。
注意这里使用getView()方法就能够,由于在父类中getView方法直接返回的对应的view实例。
/**
* 登陆界面
*/
public class LoginActivity extends BaseActivity<LoginContract.LoginView, LoginPresenter>
implements LoginContract.LoginView, View.OnClickListener {
...
@Override
protected void initActivityView(Bundle savedInstanceState) {
setContentView(R.layout.activity_login);
}
@Override
protected void findViewById() {
...
}
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter();
}
@Override
protected void getData() {
}
@Override
public void onCheckFormatSuccess() {
loading.show();
}
@Override
public void onCheckFormatFail(String info) {
RxToast.error(mContext, info).show();
}
@Override
public void onLoginSuccess(Login login) {
...
}
@Override
public void onLoginFail(String errorInfo) {
...
}
@Override
public void onClick(View view) {
...
}
...
}
复制代码
这里的LoginActivity就是登陆功能模块的view,其集成BaseActivity,传入view和presenter泛型。
实现LoginView接口,重写接口定义好的UI方法。
在createPresenter方法中建立LoginPresenter对象并返回。这样,就可使用mPresenter直接操做逻辑了。
再看下整个功能模块的事件流和数据流
大体就是这样了,有不足的地方你们多提意见。^_^
更多精彩内容,关注个人微信公众号——Android机动车