1.接口设计
V:定义数据处理规范的接口
public interface IHandler<T> {
void onBefore(); //加载前
void setData(T data); //View层调用
void onSuccess(boolean isHaveData); //加载成功
void onFailure(int code, String msg);//加载失败
}
:我们希望Activity或Fragment实现该接口来处理返回的数据
public interface IView<T> {
void bindView(View contentView);
void bindData(T data);
}
P:建立M层和V层之间的联系
public interface IPresent<T> {
void setModel(IModel<T> model);
void setContent(IView<T> adapter);
void build();
void refresh();
}
M:发起网络请求
public interface IModel<T> {
void refresh(IHandler<T> handler);//入口函数中持有IHandler的实例用于处理响应结果
}
2.核心类
public class CommonPresent<T> implements IPresent<T>, IHandler<T> {
protected View mContentView;//指向目标内容视图,一般为Activity或Fragment中的根视图
protected IModel mModel;
protected IView<T> mIview ;
public CommonPresent(View view) {
mContentView = view;
}
public CommonPresent(Context context, int layoutResId) {
this(View.inflate(context, layoutResId, null));
}
@Override
public void setModel(IModel<T> model) {
mModel = model;
}
@Override
public void setContent(IView<T> iview) {
mIview = iview;
}
@Override
public void build() {
if (mModel == null) {
throw new RuntimeException("Model is null, you need implement setModel(...)");
}
if (mIview != null) {
mIview.bindView(mContentView);
}
}
@Override
public void refresh() {
//此处为关键代码,用IModel代理CommonPresent的refresh()函数,
//并用当前CommonPresent对象作为IModel的请求的回调处理对象
mModel.refresh(this);
}
public View getContentView() {
return mContentView;
}
@Override
public void onBefore() {
}
@Override
public void onSuccess(boolean isHaveData) {
}
@Override
public void onFailure(int code, String msg) {
}
@Override
public void setData(T data) {
if (data != null) {
mIview.bindData(data);
}
}
}
3.调用
CommonPresent类主要作用是建立以上4个接口之间的逻辑关系,CommomPresent同时实现了IPresent和IHandler接口,表示它会同时具备发起请求和处理请求的能力,我们希望在该类中通过refresh()发起请求(可以看到实际上会交给IModel接口的实现类去请求),然后在通过setData()处理请求结果(实际上会交给IView的实现类处理)。以上的代码已经可以处理与业务解耦的网络请求,调用伪代码如下
public class SomeActivity implements IView<DataBean>{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
CommonPresent mPresent = new CommonPresent(context, findViewById(R.id.layout_container));
mPresent.setModel(IModelImp);//IModelImp表示IModel的实现类
mPresent.setContent(this);
mPresent.build();
mPresent.refresh();
}
@Override
public void bindView(View contentView) { //CommonPresent中定义bindView()会在build()的时候调用
//findView and initView here
}
@Override
public void bindData(DataBean bean) {
//CommonPresent中执行setData()时候会将数据实体交由IView的bindData()处理
//所以在此函数中处理结果就行,CommonPresent的setData()函数后续中会在网络请求的响应中被调用
}
}
3.Model
接下来我们只需要编写一个IModel的实现类去请求网络完后将响应结果交给IHandler处理即可
public class ModelPool{
public static IModel requestData(final int id) {
return new IModel<DataBean>() { //IView中的DataBean类型必须与这里类型一致
@Override
public void refresh(final IHandler<DataBean> handler) {
Map<String, Object> params = new HashMap();
params.put("id", id);
OkHttpUtils.post()
.url(UrlUtils.getUrl(Constants.REQUEST_COMMODITY))
.params(params)
.build()
.execute(new BaseCallback<DataBean>(handler) { //BaseCallback封装了数据解析与异常处理
@Override
public void onSuccess(DataBean bean) {
super.onSuccess(bean);
handler.setData(bean);//CommonPresent的setData()中实际调用IView的bindData()处理
}
});
}
};
}
}
这里网络请求使用的是鸿洋大神的OkHttputils,并继承CallBack自定义MyCallBack来处理数据解析和响应异常处理,代码如下:
/**GsonCallback主要用于处理数据实体的自适应泛型的解析
* 后端接口中统一返回的数据规范定义为BaseBean,解析成功后回调onSuccess(T t)
* 中可获取到具体业务对应的泛型类实体对象,失败则会回调onFail()**/
public abstract class GsonCallback<T> extends Callback<BaseBean<T>> {
public Type mType;
public GsonCallback() {
mType = getSuperclassTypeParameter(getClass());
}
public void setType(Type type) {
mType = type;
}
public static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
@Override
public BaseBean<T> parseNetworkResponse(Response response, int id) throws Exception {
String string = response.body().string();
Logger.d("data:" + string);
BaseBean<T> result = new Gson().fromJson(string, new TypeToken<BaseBean<T>>() {
}.getType());
String json = new Gson().toJson(BaseBean.getBaseBean());
if (BaseBean.getBaseBean() != null) {
T data = (T) new Gson().fromJson(json, mType);
BaseBean.setBaseBean(data);
}
return result;
}
@Override
public void onError(Call call, Exception e, int id) {
e.printStackTrace();
String BaseBean;
if (e instanceof UnknownHostException) {
BaseBean = "连接服务器失败, 请检查网络状态";
} else if (e instanceof SocketTimeoutException) {
BaseBean = "连接服务器超时";
} else {
BaseBean = "网络无法连接, 请检查网络设置!";
}
onFailure(1, BaseBean);
}
@Override
public void onResponse(BaseBean<T> response, int id) {
if (response.getCode() == 0 || response.getCode() == 3) {
onSuccess(response.getCode(), response.getMessage(), response.getBaseBean());
} else {
onFailure(response.getCode(), response.getMessage());
}
}
@Override
public void onBefore(Request request, int id) {
super.onBefore(request, id);
Logger.d(request.toString());
}
public abstract void onSuccess(int code, String msg, T t);
public abstract void onFailure(int code, String msg);
}
/**
* BaseCallback中的成功失败处理都交由了IHandler处理,通过CommonPresent中的refresh()的逻辑
* mModel.refresh(this),这里的IHandler的引用就是CommPresent当前对象,这样就实现了CommonPresent从发起
* 网络请求(实际由IModel处理)到处理请求结果(实际由IAdaper处理)的一个闭环操作。
**/
public class BaseCallback<T> extends GsonCallback<T> {
private IHandler mHandler;
public BaseCallback(IHandler handler) {
mHandler = handler;
}
@Override
public void onBefore(Request request, int id) {
mHandler.onBefore();
}
@Override
public void onSuccess(int code, String msg, T t) {
mHandler.onSuccess(t != null);
}
@Override
public void onFailure(int code, String msg) {
mHandler.onFailure(code, msg);
}
}
//BaseBean为一个典型的后端接口规范实体
public class BaseBean<T> {
int code;
String message;
T t;
}
后续继续通过拓展CommonPresent可实现对contentView中加载中、加载失败、返回为空等页面的统一封装,以及列表页面的自动分页加载(待补全)。以上还需持续完善的包括:整合IHandler和IView接口为一个接口(同为View角色);解除contentView与CommonPresent的耦合,将contentView的操作交由独立封装的BaseActivity(继承IView)处理;网络请求部分整合RxJava+Retrofit(Emmm这样的话还是重新写一个比较好)。