Android MVP框架設計(1)
阿新 • • 發佈:2019-02-01
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這樣的話還是重新寫一個比較好)。