1. 程式人生 > >基於MVP模式,設計自己的RxJava+Retrofit2+Okhttp3+Rxlifecycle開發框架

基於MVP模式,設計自己的RxJava+Retrofit2+Okhttp3+Rxlifecycle開發框架

在開發階段,如果有一個好的開發框架,不僅能提高開發效率,更能減少後期維護的時間。結合自己的實際,封裝了一套MVP+RxJava+Retrofit2+Okhttp3+Rxlifecycle+Butterknife的開發框架。架構層:V層只負責檢視的操作,P層只負責資料的互動,M層負責邏輯的處理,應該屬於完整意義上的MVP程式碼結構模式。網路層包括:普通的get/post請求,檔案上傳、檔案帶進度下載,無網路快取策略,請求帶載入的dialog等。

先直接上程式碼連結:

本項新增的依賴如下:

//Network
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.squareup.retrofit2:adapter-rxjava:2.3.0' //RxJava implementation 'io.reactivex:rxandroid:1.2.1' implementation 'io.reactivex:rxjava:1.3.0' //RxLifecycle implementation 'com.trello:rxlifecycle:0.3.0' implementation 'com.trello:rxlifecycle-components:0.3.0'
//ButterKnife implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor'com.jakewharton:butterknife-compiler:8.8.1'

MVP模式設計

MVP模式大家應該都有了解過,V層想要得到某個資料然後做檢視操作,需通過P層向M層傳送請求,M層處理後的結果回撥給P層,P層再回調給V層,期間的傳遞過程都是通過介面訪問的。


本專案的結構如下所示:


下面看看封裝MVP的思路吧。

首先是對Base各個基類的封裝。BaseAct如下所示:

public abstract class BaseAct<

V,extends BasePresenter<V>> extends RxAppCompatActivity  {
    
public Activity mActivity;
    public 
mPresenter;


    
@Override
    
public void setContentView(@LayoutRes int layoutResID) {
        
super.setContentView(layoutResID);
        
ButterKnife.bind(this);
        
mActivity this;
    
}

    
@Override
    
public void setContentView(View view) {
        
super.setContentView(view);
        
ButterKnife.bind(this);
        
mActivity this;
    
}

    
@Override
    
protected void onCreate(Bundle savedInstanceState) {
        
super.onCreate(savedInstanceState);
        
mPresenter = createPresenter();
        
mPresenter.attachView((Vthis);
        
setContentView(getLayoutId());
        
initView();
        
initData();
        
initEvent();

    
}


    
protected abstract int getLayoutId();

    protected abstract 
createPresenter();


    
/**
     * 
初始化View
     */
    
protected void initView() {
    }

    
/**
     * 
初始化資料
     
*/
    
protected void initData() {
    }

    
/**
     * 
初始化事件
     
*/
    
protected void initEvent() {
    }

    
@Override
    
protected void onDestroy() {
        
if (mPresenter != null) {
           
mPresenter.detachView();
        
}
        
super.onDestroy();

    
}


}

其中V為View的泛型,P為Presenter的泛型,RxAppCompatActivity是Rxlifecycle包下的,如下所示。該類繼承了AppCompatActivity,增加了bindUntilEvent()和bindtoLifecycle()等方法,配合網路請求繫結生命週期的方法,可以避免記憶體洩漏。mPresenter通過呼叫attachView()和detachView()實現手動繫結和解綁,也起到了防止記憶體洩漏的作用。

public class RxAppCompatActivity extends AppCompatActivity implements ActivityLifecycleProvider {

    private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();

    @Override
    public final Observable<ActivityEvent> lifecycle() {
        return lifecycleSubject.asObservable();
    }

    @Override
    public final <T> Observable.Transformer<T, T> bindUntilEvent(ActivityEvent event) {
        return RxLifecycle.bindUntilActivityEvent(lifecycleSubject, event);
    }

    @Override
    public final <T> Observable.Transformer<T, T> bindToLifecycle() {
        return RxLifecycle.bindActivity(lifecycleSubject);
    } 

BasePresenter如下所示:

public abstract class BasePresenter<V> implements Presenter<V> {

    protected WeakReference<V> mMvpView;

    @Override
    public void attachView(V mvpView) {
        this.mMvpView = new WeakReference<>(mvpView);
    }


    protected V getView() {
        return mMvpView.get();
    }


    @Override
    public void detachView() {
        if (mMvpView != null) {
            mMvpView.clear();
            mMvpView = null;
        }
    }

BasePresenter將BaseAct繫結的View檢視包裝成弱引用,防止了記憶體洩漏(三重保障,穩穩的不洩漏)。Presenter是一個提供方法的介面,如下:

public interface Presenter<V> {

    void attachView(V view);

    void detachView();

}

BaseModule如下所示:

public class BaseModule {

    protected BaseAct mActivity;
    protected BaseFrag mFragment;

    public BaseModule(BaseAct act){
        this.mActivity=act;
    }

    public BaseModule(BaseFrag frag){
        this.mFragment=frag;
    }

    protected  <T> void addActSubscribe(Observable<T> observable,Subscriber<T> subscriber ) {
        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(mActivity.<T>bindUntilEvent(ActivityEvent.DESTROY))//繫結生命週期,防止記憶體洩露
                .subscribe(subscriber);
    }

    protected  <T> void addFragSubscribe(Observable<T> observable,Subscriber<T> subscriber ) {
        observable.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(mFragment.<T>bindUntilEvent(FragmentEvent.DESTROY))//繫結生命週期,防止記憶體洩露
                .subscribe(subscriber);
    }


}

 裡面將原始的網路請求方法抽了出來,並持有Act或者Frag的物件。只要子類繼承了BaseModule,就可以在Module層為所欲為處理自己想要的邏輯了。

下面是具體的MainAct,MainPresenter,MainModelImp,如下所示:

public class MainAct extends BaseAct<MainView, MainPresenter> implements MainView {

    @BindView(R.id.bt)
    Button bt;
    @BindView(R.id.bt2)
    Button bt2;
    @BindView(R.id.bt3)
    Button bt3;
    @BindView(R.id.bt4)
    Button bt4;
    @BindView(R.id.bt5)
    Button bt5;
    @BindView(R.id.bt6)
    Button bt6;
    @BindView(R.id.tv)
    TextView tv;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected MainPresenter createPresenter() {
        return new MainPresenter(this);
    }


    /**
     * 點選事件 業務請求
     */
    @OnClick({R.id.bt, R.id.bt2, R.id.bt3, R.id.bt4, R.id.bt5, R.id.bt6})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.bt://get請求
                mPresenter.SimpleGet();
                break;
            case R.id.bt2://post請求
                mPresenter.SimplePost();
                break;
            case R.id.bt3://單圖上傳
                mPresenter.fileUpload();
                break;
            case R.id.bt4://多圖上傳
                mPresenter.fileUploads();
                break;
            case R.id.bt5://檔案帶進度下載
                mPresenter.fileDownLoad();
                break;
            case R.id.bt6://無網路取快取,測試時將網路關閉
                mPresenter.simpleGetCache();
                break;
        }
    }

    //------------------------------業務回撥---------------------------

    /**
     * get請求回撥
     */
    @Override
    public void simpleGetCallback(String str) {
        tv.setText(String.valueOf("get成功請求返回資料: " + str));
    }


    /**
     * Post請求回撥
     */
    @Override
    public void simplePostCallback(JsonObject jsonObject) {
        tv.setText(String.valueOf("post成功請求返回資料: " + jsonObject));
    }


    /**
     * 單圖上傳回調
     */
    @Override
    public void fileUploadCallback(JsonObject jsonObject) {
        tv.setText(String.valueOf("單圖上傳成功返回資料: " + jsonObject));
    }


    /**
     * 多圖上傳回調
     */
    @Override
    public void fileUploadsCallback(JsonObject jsonObject) {
        tv.setText(String.valueOf("多圖上傳成功返回資料: " + jsonObject));
    }


    /**
     * 檔案下載回撥
     */
    @Override
    public void fileDownLoadCallback() {
        tv.setText(String.valueOf("檔案下載成功"));
    }

    /**
     * 無網路取快取回撥
     */
    @Override
    public void noNetworkCacheCallback(String str) {
        tv.setText(String.valueOf("無網路快取資料: " + str));
    }


}

 裡面只有mPresenter請求和回撥成功後檢視的操作,感覺還是很清爽的。

 MainPresenter:

public class MainPresenter extends BasePresenter<MainView> implements MainModel.LoadingCallBack {

    private MainModel mMainModelImp;

    public MainPresenter(BaseAct act) {
        mMainModelImp = new MainModelImp(act);
    }

    //------------------------V層請求-------------------------------

    /**
     * 普通get請求
     */
    public void SimpleGet() {
        mMainModelImp.getSimpleData(this);
    }

    /**
     * 普通post請求
     */
    public void SimplePost() {
        mMainModelImp.getPostData(this);
    }


    /**
     * 單圖上傳上傳
     */
    public void fileUpload() {
        mMainModelImp.fileUpload(this);
    }

    /**
     * 多圖片上傳
     */
    public void fileUploads() {
        mMainModelImp.fileUploads(this);
    }

    /**
     * 檔案帶進度下載
     */
    public void fileDownLoad() {
        mMainModelImp.downLoadFile(this);
    }

    /**
     * 無網路取快取
     */
    public void simpleGetCache() {
        mMainModelImp.getSimpleCacheData(this);
    }

    //---------------------M層回撥-----------------------------------

    /**
     *
     */
    @Override
    public void simpleDataCompleted(String data) {
        getView().simpleGetCallback(data);
    }

    @Override
    public void simplePostCompleted(JsonObject jsonObject) {
        getView().simplePostCallback(jsonObject);
    }

    @Override
    public void fileUploadCompleted(JsonObject jsonObject) {
        getView().fileUploadCallback(jsonObject);
    }

    @Override
    public void fileUploadsCompleted(JsonObject jsonObject) {
        getView().fileUploadsCallback(jsonObject);
    }

    @Override
    public void downLoadFileCompleted() {
        getView().fileDownLoadCallback();
    }

    @Override
    public void simpleCacheDataCompleted(String data) {
        getView().noNetworkCacheCallback(data);
    }


}

MainPresenter只負責將V層傳過來的命令傳送給M層,然後將M層處理的結果回撥給V層,其中沒有任何的邏輯操作,P層的職責是很明確的。

MainModelImp:

public class MainModelImp extends BaseModule implements MainModel {


    public MainModelImp(BaseAct act) {
        super(act);
    }

    /**
     * Mget請求的方法,帶dialog形式請求
     */
    @Override
    public void getSimpleData(final LoadingCallBack callBack) {
        addActSubscribe(HttpClient.getService(AppService.class).simpleGet(), new RxRequestCallBack<String>(mActivity) {
            @Override
            public void onSuccess(HttpResult<String> httpResult) {
                callBack.simpleDataCompleted(httpResult.getData());
            }
        });
    }

    /**
     * Mpost請求資料的方法
     */
    @Override
    public void getPostData(final LoadingCallBack callBack) {

        String GROUP_ID = "298cea3dabeb1545004451982d6c04f6";
        addActSubscribe(HttpClient.getService(AppService.class).simplePost(GROUP_ID), new RxRequestCallBack<JsonObject>() {
            @Override
            public void onSuccess(HttpResult<JsonObject> httpResult) {
                callBack.simplePostCompleted(httpResult.getData());
            }
        });

    }

    /**
     * post單圖上傳
     */
    @Override
    public void fileUpload( final LoadingCallBack callBack) {
        Bitmap bitmap = BitmapFactory.decodeResource(mActivity.getResources(), R.mipmap.ic_launcher);
        byte[] bytes = BitmapUtil.bitmapToBytes(bitmap);//拿到陣列
        UploadUtil.Builder builder = new UploadUtil.Builder().
                addByte("upload", bytes);//檔案上傳工具類
        addActSubscribe(HttpClient.getService(AppService.class).uploadPic(builder.build()), new RxRequestCallBack<JsonObject>() {
            @Override
            public void onSuccess(HttpResult<JsonObject> httpResult) {
                callBack.fileUploadCompleted(httpResult.getData());
            }
        });
    }

    /**
     * post多圖上傳
     */
    @Override
    public void fileUploads( final LoadingCallBack callBack) {
        Bitmap bitmap = BitmapFactory.decodeResource(mActivity.getResources(), R.mipmap.ic_launcher);
        byte[] bytes = BitmapUtil.bitmapToBytes(bitmap);//拿到陣列
        UploadUtil.Builder builder = new UploadUtil.Builder();
        //多張圖片
        for (int i = 0; i < 3; i++) {
            builder.addByte("image[]", bytes, i);
        }
        addActSubscribe(HttpClient.getService(AppService.class).uploadPics(builder.build()), new RxRequestCallBack<JsonObject>() {
            @Override
            public void onSuccess(HttpResult<JsonObject> httpResult) {
                callBack.fileUploadsCompleted(httpResult.getData());
            }
        });
    }

    /**
     * 檔案下載
     */
    @Override
    public void downLoadFile(final LoadingCallBack callBack) {
        String fileName = "app.apk";
        File externalFilesDir = ContextUtil.getContext().getExternalFilesDir(null);//外部儲存的私有目錄,應用刪除後此檔案也會被刪除
        final FileCallBack<ResponseBody> downLoadCallback = new FileCallBack<ResponseBody>(externalFilesDir.toString(), fileName) {

            @Override
            public void onSuccess(ResponseBody responseBody) {
                callBack.downLoadFileCompleted();
            }

            @Override
            public void progress(long progress) {
                LogUtil.e("progress: " + progress / 1024 + "kb  total: " + FileLoadEvent.getInstance().getTotal() / 1024 + "kb");
            }

            @Override
            public void onStart() {
                LogUtil.e("onStart");
            }

            @Override
            public void onCompleted() {
                LogUtil.e("onCompleted");
            }

            @Override
            public void onError(Throwable e) {
                if (e instanceof SocketTimeoutException) {
                    LogUtil.e("SocketTimeoutException: 網路中斷,請檢查您的網路狀態");
                } else if (e instanceof ConnectException) {
                    LogUtil.e("ConnectException: 網路中斷,請檢查您的網路狀態");
                } else if (e instanceof UnknownHostException) {
                    LogUtil.e("UnknownHostException: 網路中斷,請檢查您的網路狀態");
                } else {
                    LogUtil.e("onError:其他錯誤:" + e.getMessage() + "  cause: " + e.getCause());
                }
                e.printStackTrace();
            }
        };
        //重寫了ResponseBodyHttpClient
        String URL = "http://download.fir.im/v2/app/install/5818acbcca87a836f50014af?download_token=a01301d7f6f8f4957643c3fcfe5ba6ff";
        DownLoadHttpClient.getService(AppService.class).download(URL)
                .subscribeOn(Schedulers.io())//請求網路 在排程者的io執行緒
                .observeOn(Schedulers.io()) //指定執行緒儲存檔案
                .doOnNext(new