基於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<
public Activity mActivity;
public P 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((V) this);
setContentView(getLayoutId());
initView();
initData();
initEvent();
}
protected abstract int getLayoutId();
protected abstract P 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); } /** * M層get請求的方法,帶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()); } }); } /** * M層post請求資料的方法 */ @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(); } }; //重寫了ResponseBody的HttpClient 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