关于网上对mvc框架的介绍有很多,例子也很多。因为框架都是人用的,所以每个人都有每个人的见解。由于最近有大把的时间,巩固下基础和加深下理解【以下都是个人理解,可以借鉴,有自己的想法】。
mvc的优点:很好的将model和view层分离,降低耦合,降低代码块之间相互影响,提高代码复用及扩展;
缺点是:如果完全按照mvc框架开发,有些地方会变得臃肿。而且view层和controller没有完全解耦
首先我觉得开发框架是为了更好的开发,最好是借鉴开发思路,灵活运用。比如,你封装了一个PreferenceUtil用来存储一些轻量的数据,这个时候存储只要调用PreferenceUtil.put就行了。这个时候完全按照mvc的话,意思要把数据处理这块放在model会显得比较多余,个人见解。
【View(视图层)】:
与用户交互,响应用户操作。可理解为,layout布局。同时activity里有view的引用,有些博客说model拿到数据交给view,有些又说交给controller,然后交给view。因为Activity既有Control又有View,所以Control和View有少量的耦合性;在这里我先将activiity在mvc中是定位成controller(控制器)的。只要知道Controller和View有这一层关系就可以了。
【Controller(控制器)】
响应用户操作,并通知model数据等处理
【Model(模型)】
Model接收Controller的通知后,独立运行,请求数据等,通过接口将数据返回
先发一个项目链接基于mvc的思路的封装。
好多人把网络请求什么的封装在BaseActivity和BaseFragment,我个人觉得只是借鉴了mvc的思路,加入是dialog里要请求网络呢,而且封装在base里。BaseActivity和BaseFragment要写2遍了。所以这个时候要用一个Model将这部分独立出来像这样:
public abstract class ModelBase {
//okhttp get请求
public void sendOkHttpGet(final ParamsBuilder paramsBuilder, final NetWorkListener netWorkListener) {
final Context context;
if (netWorkListener instanceof AppCompatActivity) {
context = (AppCompatActivity) netWorkListener;
} else if (netWorkListener instanceof Fragment) {
context = ((Fragment) netWorkListener).getActivity();
} else {
context = paramsBuilder.getContext();
}
String tag = paramsBuilder.getTag();
if (TextUtils.isEmpty(tag)) {
//不设置tag,默认用类路径名。
tag = netWorkListener.getClass().toString();
}
EasyOk.get()
.url(paramsBuilder.getUrl())
.tag(tag)
//内部已经做了null处理
.headers(paramsBuilder.getHeads())
//内部已经做了null处理
.params(paramsBuilder.getParams())
//默认不缓存
.cacheOfflineTime(paramsBuilder.getCacheOfflineTime())
.cacheOnlineTime(paramsBuilder.getCacheOnlineTime())
//默认只请求一次
.onlyOneNet(paramsBuilder.isOnlyOneNet())
//默认不重连
.tryAgainCount(paramsBuilder.getTryAgainCount())
.build()
.enqueue(new ResultCall() {
@Override
public void onBefore() {
if (paramsBuilder.isShowDialog()) {
if (context == null) {
throw new NullPointerException("context is null");
}
LoadingDialog.getInstance().show(context, paramsBuilder.getLoadMessage());
}
}
@Override
public void onAfter() {
LogUtils.i("我难道没有走吗", "11111111111");
LoadingDialog.getInstance().dismiss();
}
@Override
public void onError(String message) {
if (paramsBuilder.isOverrideError()) {
NetFailBean errorBean = new NetFailBean(message);
netWorkListener.onNetCallBack(paramsBuilder.getCommand(), errorBean);
} else {
// 不重写那么只弹提示
ToastUtils.showToast(message);
}
}
@Override
public void onSuccess(final String response) {
Type type = paramsBuilder.getType();
if (type == null) {
//如果type不带,那么返回string
EasyOk.getInstance().getmDelivery().post(new Runnable() {
@Override
public void run() {
netWorkListener.onNetCallBack(paramsBuilder.getCommand(), response);
}
});
return;
}
Object successBean = null;
try {
successBean = GsonUtil.deser(response, type);
} catch (Exception e) {
LogUtils.i("网络请求", "解析出错了 ==> 参数类型可能有误");
return;
}
if (((ResponModel) successBean).getStatus() == 1) {
//我以前的接口定义的是1是成功,例如点关注,1关注成功,0关注失败。这里网络code都是200,注意
final Object finalSuccessBean1 = successBean;
EasyOk.getInstance().getmDelivery().post(new Runnable() {
@Override
public void run() {
netWorkListener.onNetCallBack(paramsBuilder.getCommand(), ((ResponModel) finalSuccessBean1).getBody());
}
});
} else {
if (paramsBuilder.isSuccessErrorOverrid()) {
final Object finalSuccessBean = successBean;
EasyOk.getInstance().getmDelivery().post(new Runnable() {
@Override
public void run() {
ErrorBean errorBean = new ErrorBean(((ResponModel) finalSuccessBean).getMessage());
netWorkListener.onNetCallBack(paramsBuilder.getCommand(), errorBean);
}
});
} else {
//不重写默认都是弹提示,考虑到用户有其他操作,可能不弹提示,有其他操作考虑
final Object finalSuccessBean2 = successBean;
EasyOk.getInstance().getmDelivery().post(new Runnable() {
@Override
public void run() {
ToastUtils.showToast(((ResponModel) finalSuccessBean2).getMessage());
}
});
}
}
}
@Override
public void inProgress(float progress) {
}
});
}
}
我用一个抽象类把具体的网络请求和解析封装起来了。网上大量讲解mvc的都说一个接口一个model,我在想如果这部分统一的代码都要在每个model了写一遍,真的会累死人了。同时每个接口一个model,接口量一大,我相信,你去找接口请求,和把网络请求写在controller没有多大区别 。
所以我的思路是:把统一操作用ModelBase封装起来,联网请求和数据分析。然后可以用一个单例把ModelSuperImpl把所有的需要联网请求调用的方法放进去,界面调用只需要传参数和网络请求结果回调即可:
public class ModelSuperImpl extends ModelBase {
private static final ModelSuperImpl ourInstance = new ModelSuperImpl();
public static ModelSuperImpl netWork() {
return ourInstance;
}
private ModelSuperImpl() {
}
public void gankGet(ParamsBuilder paramsBuilder, NetWorkListener netWorkListener) {
paramsBuilder.url(SystemConst.GANK_GET)
// .type(new TypeToken<ResponModel<String>>() {
// }.getType())
;
sendOkHttpGet(paramsBuilder, netWorkListener);
}
}
这样一个ModelSuperImpl就可以搞定,可以把所有网络请求调用方法放在这里面。也方便查找之类的。当然,假如我们有数据存储和权限申请,当然可以在写一个Model,将不同功能逻辑业务代码分割开还是有必要的,下面以权限申请为例,可以用ModelPermissionImpl去封装,如:
public class ModelPermissionImpl {
//有可能一个页面进行多个网络请求,加上一个指令type区分
public void requestPermission(final int permissionCommand, final FragmentActivity activity, final PermissionListener permissionListener, final String... permission) {
//如果权限都被申请了,那么不再继续
if (checkPermission(activity, permission)) {
permissionListener.permissionSuccess(permissionCommand);
return;
}
RxPermissions rxPermissions = new RxPermissions(activity);
rxPermissions.request(permission).subscribe(new Consumer<Boolean>() {
@Override
public void accept(Boolean aBoolean) throws Exception {
if (aBoolean) {
//有些手机保持禁止以后会出现aBoolean为true的情况,则加上下一句
if (!checkPermission(activity, permission)) {
permissionListener.permissionFail(permissionCommand);
return;
}
permissionListener.permissionSuccess(permissionCommand);
} else {
permissionListener.permissionFail(permissionCommand);
}
}
});
}
public boolean checkPermission(Context context, String... permission) {
for (int i = 0; i < permission.length; i++) {
if (ActivityCompat.checkSelfPermission(context, permission[i]) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
}
权限请求成功需要的实现的接口是PermissionListener;由于操作的方便性,我们也可以把ModelPermissionImpl的调用放进单例ModelSuperImpl里,这样调用起来,直接用一个类搞定,不会那么紊乱。像这样:
public class ModelSuperImpl extends ModelBase {
private static final ModelSuperImpl ourInstance = new ModelSuperImpl();
public static ModelSuperImpl netWork() {
return ourInstance;
}
public static ModelPermissionImpl permission() {
return new ModelPermissionImpl();
}
private ModelSuperImpl() {
}
}
在你用到网络请求和权限申请的页面只需要实现接口NetWorkListener和PermissionListener;网络请求调用ModelSuperImpl.netWork.;权限申请调用ModelSuperImpl.permission.
以上大部分代码多余代码都去掉了,便于理解。这些观点都是我个人对mvc的一些见解。这个时候配合BaseActivity和BaseFragment实现网络回调接口更佳。如果理解有误,欢迎留言指正