Hement:MVP架構中的網絡框架(RxJava2+Retrofit2+RxAndroid)(二)

  • 如何設計好網絡框架,我心中也沒答案,看過不少別人的輪子,有些太簡單,有限過於複雜,有些爲了知足去請求網絡,褒貶不一
  • 本文的網絡框架基於我三年前的網絡框架的升級版,具體請看這裏 MVP網絡框架(Retorfit+Rxjava+Rxandroid)

加入依賴

api "com.squareup.retrofit2:retrofit:2.5.0"
    api "com.squareup.retrofit2:converter-gson:2.5.0"
    api "com.squareup.retrofit2:adapter-rxjava2:2.5.0"
    api 'io.reactivex.rxjava2:rxandroid:2.1.0'
    api 'io.reactivex.rxjava2:rxjava:2.2.2'
    api "com.squareup.okhttp3:logging-interceptor:3.12.0"
複製代碼
  • rxjava:是反應式擴展的Java VM實現:一個用於使用可觀察序列組成異步和基於事件的程序的庫。
  • rxandroid:這個模塊向RxJava添加了最少的類,使得在Android應用程序中編寫反應性組件變得簡單且無麻煩。更具體地說,它提供了在主線程或在任何Looper上調度的調度器。
  • converter-gson:經過GsonConverterFactoryRetrofit添加Gson支持
  • logging-interceptor:能夠很直觀的觀察到網絡請求的日記
    image.png
    image.png
  • adapter-rxjava2:一個用於適應RXJava 2類型的適配器。

搭建網絡框架

  • IMvpView
public interface IMvpView {

  /**
   * 數據失敗的同一處理
   * @param errorCode
   * @param errorMsg
   */
  void getDataFail(String errorCode, String errorMsg);

  /**
   * 發成異常的同一處理
   * @param e
   */
  void onError(Throwable e);
}
複製代碼
  • IPresenter 處理和View的生命週期
public interface IPresenter<V extends IMvpView> {

    /**
     * 和view結合
     * @param mvpView
     */
    void attachView(V mvpView);

    /**
     * 和view 斷開
     */
    void detachView();
}
複製代碼
  • BasePresenter 全部Presenter的基類,同時綁定全部Activity的生命週期
public class BasePresenter<V extends IMvpView> implements IPresenter<V> {


    private V mMvpView;

    @Override
    public void attachView(V mvpView) {
        mMvpView = mvpView;
    }

    @Override
    public void detachView() {
        mMvpView = null;
    }

    /**
     * 判斷是否還在鏈接在一塊兒的
     * @return
     */
    public boolean isViewAttached() {
        return mMvpView != null;
    }

    public V getMvpView() {
        return mMvpView;
    }

    /**
     * 檢查View是否附着
     */
    public void checkViewAttached() {
        if (!isViewAttached()) throw new MvpViewNotAttachedException();
    }

    public static class MvpViewNotAttachedException extends RuntimeException {
        public MvpViewNotAttachedException() {
            super("在綁定數據以前必定要綁定視圖");
        }
    }

    /**
     * 這是Observer 中的 onServer ,當咱們調用這個方法,直接就不會走到 onNext中去
     * @param disposable
     */
    public  void dispose(Disposable disposable) {
        if (disposable != null && !disposable.isDisposed()) {
            disposable.dispose();
        }
    }
}
複製代碼
  • 整體結構圖
    整體結構圖

網絡框架DataManager

  • 根據上篇文章的設計思路 Hement:MVP架構設計(一)
    Android中的MVP模式.jpg
  • 能夠知道DataManager不單單是處理網絡請求那麼簡單,同時還須要處理本地存儲SharedPreferencesSQLite
  • 因此就轉變成了,DataManager向外界提供數據對象,數據的處理已經幫您處理好了,使用的時候只須要獲取你想要的對象實例,就能夠了,具體封裝以下
@Singleton
public class DataManager {


    private final IRemoteServer mIRemoteServer;
    private final PreferencesHelper mPreferencesHelper;
    private final DatabaseHelper mDatabaseHelper;

    @Inject
    public DataManager(IRemoteServer server, PreferencesHelper preferencesHelper, DatabaseHelper databaseHelper) {
        mIRemoteServer = server;
        mPreferencesHelper = preferencesHelper;
        mDatabaseHelper = databaseHelper;
    }

    public void loadData(String key, String day, BaseObserver observer) {
        Observable<SMResponse<ArrayList<TodayBean>>> today = mIRemoteServer.getToday(key, day);
        today.observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(observer);
    }

    public PreferencesHelper getPreferencesHelper() {
        return mPreferencesHelper;
    }

    /**
     * 從網絡獲取數據,緩存到數據庫,而後從數據庫中取數據
     * @return
     */
    public Observable<TodayBean> syncDBBean() {
        return mIRemoteServer.getToday("b15674dbd34ec00ded57b369dfdabd90", "1/1").concatMap(new Function<SMResponse<ArrayList<TodayBean>>, ObservableSource<? extends TodayBean>>() {
            @Override
            public ObservableSource<? extends TodayBean> apply(@NonNull SMResponse<ArrayList<TodayBean>> response)
                    throws Exception {
                return mDatabaseHelper.setDBData(response.result);
            }
        });
    }

    /**
     * 從數據庫中取數據
     * @return
     */
    public Observable<List<TodayBean>> getDBBean() {
        return mDatabaseHelper.getDBData().distinct();
    }
}

複製代碼

網絡框架如何使用

  • 這裏使用的是聚合數據的接口 ,在這裏感謝無私的貢獻,三年了這個接口還能用,萬分感謝
http://v.juhe.cn/todayOnhistory/queryEvent.php?key=b15674dbd34ec00ded57b369dfdabd90&date=1/1
複製代碼
  • 一、定義遠程Server:IRemoteServer,在這裏我把之前的單獨生成的RetrofitServer抽取到IRemoteServer,由於項目使用了Dagger2,後面介紹
public interface IRemoteServer {
    /**
     * http://v.juhe.cn/todayOnhistory/queryEvent.php?key=b15674dbd34ec00ded57b369dfdabd90&date=1/1
     * @param key 申請的key
     * @param date 日期
     * @return 返回歷史上的今天
     */
    @GET("queryEvent.php")
    Observable<SMResponse<ArrayList<TodayBean>>> getToday(@Query("key") String key, @Query("date") String date);

    class Creator {
        public static IRemoteServer newHementService() {
          return  SMRetrofit.getService(HementApplication.getContext(), IRemoteServer.class);
        }
    }
}
複製代碼
  • 二、DataManager中的線程調度,結果回調到AndroidSchedulers.mainThread()線程,就是安卓的UI線程。
private final IRemoteServer mIRemoteServer;
    private final PreferencesHelper mPreferencesHelper;
    private final DatabaseHelper mDatabaseHelper;

    @Inject
    public DataManager(IRemoteServer server, PreferencesHelper preferencesHelper, DatabaseHelper databaseHelper) {
        mIRemoteServer = server;
        mPreferencesHelper = preferencesHelper;
        mDatabaseHelper = databaseHelper;
    }

    public void loadData(String key, String day, BaseObserver observer) {
        Observable<SMResponse<ArrayList<TodayBean>>> today = mIRemoteServer.getToday(key, day);
        today.observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(observer);
    }
複製代碼
  • 三、NetWorkPresenter定義
@ConfigPersistent
public class NetWorkPresenter  extends BasePresenter<NetWorkView> {

    private final DataManager mDataManager;

    private Disposable mDisposable;

    @Inject
    public NetWorkPresenter(DataManager dataManager) {
        mDataManager = dataManager;
    }

    @Override
    public void attachView(NetWorkView mvpView) {
        super.attachView(mvpView);
    }

    @Override
    public void detachView() {
        super.detachView();
        if (mDisposable != null) mDisposable.dispose();
    }
    public void loadData(String key,String day){
        //檢查View是否附着在上面,不在直接拋出異常
        checkViewAttached();
        //檢查是否往下運行
        dispose(mDisposable);
        mDataManager.loadData(key,day,new BaseObserver<SMResponse<ArrayList<TodayBean>>>(new SubscriberListener<SMResponse<ArrayList<TodayBean>>>() {

            @Override
            public void onSubscribe(Disposable disposable) {
                super.onSubscribe(disposable);
                mDisposable = disposable;
            }

            @Override
            public void onSuccess(SMResponse<ArrayList<TodayBean>> response) {
                getMvpView().getDataSuccess(response.result);
            }

            @Override
            public void onFail(String errorCode, String errorMsg) {
                getMvpView().getDataFail(errorCode,errorMsg);
            }

            @Override
            public void onError(Throwable e) {
                getMvpView().onError(e);
            }
        }));
    }
}

複製代碼
  • 四、在Activity中的使用,經過Dagger2依賴注入NetWorkPresenter,同時在 onDestroy()中,銷燬RxJava2正在調度的任務,我的理解的就是,關閉Activity,網絡請求也有多是在執行中,因此須要銷燬。
public class NetWorkActivity extends BaseActivity implements NetWorkView, View.OnClickListener {

    String key="b15674dbd34ec00ded57b369dfdabd90";
    @Inject
    NetWorkPresenter mMainPresenter;

    private Button mBtn;
    private EditText mDay;
    private EditText mMonth;
    private RecyclerView mRecyclerView;
    private SMAdapter mSmAdapter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activityComponent().inject(this);
        setContentView(R.layout.activity_net_work);
        Timber.tag(getClassName()).i("mMainPresenter =%s",mMainPresenter);
        mMainPresenter.attachView(this);
        initView();
        initListener();
    }
    private void initView() {
        mBtn = (Button) findViewById(R.id.btn);
        mMonth = (EditText) findViewById(R.id.et_month);
        mDay = (EditText) findViewById(R.id.et_day);
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
    }
    private void initListener() {
        mBtn.setOnClickListener(this);
        mSmAdapter = new SMAdapter(this, null);
        mRecyclerView.setAdapter(mSmAdapter);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    }

    @Override
    public void getDataFail(String errorCode, String errorMsg) {
        Toast.makeText(this,errorMsg+errorCode,Toast.LENGTH_LONG).show();
    }

    @Override
    public void onError(Throwable e) {
        Toast.makeText(this,e.toString(),Toast.LENGTH_LONG).show();
    }

    @Override
    public void getDataSuccess(ArrayList<TodayBean> result) {
        String s = new Gson().toJson(result);
        Timber.tag(getClassName()).i(s);
        Thread thread = Thread.currentThread();
        Timber.tag(getClassName()).i(thread.toString());
        mSmAdapter.addData(result);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMainPresenter.detachView();
    }

    @Override
    public void onClick(View v) {
        if (TextUtils.isEmpty(mMonth.getText())||TextUtils.isEmpty(mDay.getText())){
            Toast.makeText(NetWorkActivity.this,"不能爲空",Toast.LENGTH_SHORT).show();
        }else {
            mMainPresenter.loadData(key,mMonth.getText()+"/"+mDay.getText());
        }
    }
}
複製代碼
相關文章
相關標籤/搜索