1. 程式人生 > >Android MVP框架設計(1)

Android MVP框架設計(1)

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這樣的話還是重新寫一個比較好)。