眼下Retrofit+RxJava搭配的網絡請求框架很是流行,本着學習的態度,寫了一個相關的demo。寫着寫着就想朝着搭建一個項目框架的方向走。於是使用了一下MVP模式。
RxJava 確實挺好用,個人特別喜歡這種“流式”的代碼風格,邏輯很清晰,起碼提供了一種相對的規範,開發者按照對應的流程寫代碼,後期的維護和拓展會簡單很多。
MVP模式簡單說就是爲了解耦,各行各職,閱讀代碼,拓展功能代價不會那麼大(或許有些人認爲沒必要用MVP,直接在activity/fragment中寫代碼就好了,那隻能說你沒遇到到過相對大一點的項目,或者沒遇到“實習生”寫代碼,那酸爽,看代碼會看得你懷疑人生)
MVP
在使用MVC開發Android應用的時候,原理上
- View:對應於佈局文件xml
- Model:業務邏輯和實體模型
- Controllor:對應於Activity
但是寫代碼的時候,你就會發現,好多跟view相關的操作都在activity中實現完成了,導致activity既是Controllor又是View,網上有人稱爲是MV模式,也因此導致activity的代碼量特別大,1000+的代碼很常見
後來,Presenter的出現,將Actvity,xml 視爲View層,Model不變,Presenter負責完成View層與Model層的交互。於是MVP是這樣的:
- View 對應於Activity,xml 負責View的繪製以及與用戶交互
- Model 依然是業務邏輯和實體模型
- Presenter 負責完成View於Model間的交互
在網上看了一下MVP的使用demo,挺多人將一個頁面需要完成的操作,以及需要用到的控件全部定義到相關的View接口中,示例:
public interface IUserLoginView
{
String getUserName();
String getPassword();
void clearUserName();
void clearPassword();
void showLoading();
void hideLoading();
void toMainActivity(User user);
void showFailedError();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
個人覺得,這個有點蛋疼
1.這樣view接口需要的方法太多了,有些實現(clearUserName())可以放在activity中操作,第一不需將控件通過接口傳到Presenter中,第二我認爲這種算是對View的操作,還是可以看作View相關的。
2.我們很難知道一個界面都要實現些什麼方法(如果包括對某個控件內容清空等),但是我們不難知道一個activity需要實現哪些主要的功能,比如登錄頁面就一個登錄功能,或者再加多一個第三方登錄咯。
所以我覺得View接口中定義一些常用的方法,以及一些需要實現的方法就可以了,通過回調內容,把控件賦值,數據展示等還是放回在activity中操作,presenter只需要將對應的實體或者數據給activity就好了,activity怎麼展示,不用管,不關我的事情,做到各行各職。
或許有人會說,咦~你這都不是MVP,網上的MVP這些操作要放在presenter中的,這時我上去就給你一巴掌,我們使用優秀的框架/架構是爲了學習它優秀的模式或者編碼風格,如果一味的按部就班照着用,那將毫無意義!同時,我們只有不斷改進,不斷推成出新才能使得技術不斷進步,如果前人不對MCV提出質疑,就不會有今天的MVP。
所以我覺得View的接口應該這樣
一個BaseView,定義常用的方法,其他頁面View接口繼承基類
public interface IBaseView {
//顯示loading
void showLoading();
//關閉loading
void closeLoading();
//顯示吐司
void showToast(String msg);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
這個基類怎麼寫看項目需要,按照開發者各自需求編寫。
現在寫一個登錄的LoginView,繼承BaseView同時添加特定的接口
public interface ILoginView extends IBaseView {
//顯示結果
void showResult(UserBean bean);
}
- 1
- 2
- 3
- 4
- 5
- 6
這裏定義一個showResult(UserBean bean)
將User實體類傳給activity,用於展示用戶信息。
寫到這裏的時候的我忽然更加堅定我所理解的MVP是對的,解耦嘛
Presenter:負責獲取或者構建UserBean
Activity:負責展示Presenter給過來的數據
之前看到過有人通過View接口將activity的控件幾乎“拷貝”到了presenter中,雖然實現了邏輯處理在Presenter,但是如果Presenter邏輯改動還是會牽一髮動全身,要改動很多
現在這種方式挺好的,負責構建數據,不參與展示,也方便單元測試。對,就是這樣的。
Retrofit2+RxJava2+RxLifecycle2
Retrofit+RxJava確實是一種很不錯的搭配,RxJava可以指定運行的線程,在網絡請求時,開啓線程耗時操作,響應結果時切換爲主線程操作UI。非常漂亮,代碼風格也贊,我個人稱爲流式操作,從上到下一步步代表操作的主要邏輯,比起傳統的迷之嵌套,迷之縮進好多了。
我們知道RxJava使用訂閱模式,如果沒有及時取消訂閱,會導致內存泄漏,這個是非常糟糕的行爲,當然解決方式也很簡單,在對應的生命週期取消訂閱就好,不過我還是懷着好奇之心Github一下,果然已經有人對此作出了貢獻RxLifecycle
通過綁定生命週期可以很方便的管理訂閱與取消訂閱。
Github: https://github.com/trello/RxLifecycle
Retrofit+RxJava的使用還是挺簡單的,不過相對於大家已經用在項目的網絡請求框架,它還是需要進行加工的。比如說錯誤處理,配合RxLifecycle使用,以及很多人會問的,在MVP中使用的時候怎麼取消訂閱。
先看下最簡單的使用
接口代碼
public interface TestApi {
@GET("v1/mobile/address/query")
Observable<String> request(@QueryMap Map<String, Object> request);
}
- 1
- 2
- 3
- 4
使用代碼
Map<String, Object> request = new HashMap<>();
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
.baseUrl("http://apicloud.mob.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
retrofit.create(TestApi.class).request(request).subscribe(new Observer<String>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull String s) {
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
代碼簡介,清晰,構建請求參數,構建被觀察者對象,以及傳入一個觀察者對象實現
訂閱回調onSubscribe
也可以是開始的操作
成功回調onNext
失敗回調onError
監聽完成onComplete
或許開發者一眼就看出了onError
回調的對象是Throwable
這個不能忍啊,投入使用的框架肯定得封裝,那就從這裏開始
錯誤處理
在常見的網絡請求框架中一般會有兩個回調函數
/**
* 錯誤/異常回調
*/
protected abstract void onError(ApiException e);
/**
* 成功回調
*/
protected abstract void onSuccess(T response);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
定義onError
回調函數觸發的場景是:1.異常2.錯誤
1.異常:請求異常,解析數據出錯,網絡異常等等
2.錯誤:某一次請求邏輯錯誤,(例如:登錄錯誤)
將上述兩種情況交給onError
回調函數處理
在請求邏輯成功的時候觸發一個onSuccess
函數。這樣監聽者就只需要兩個函數,一個失敗,一個成功,失敗提示給用戶,成功負責展示數據,跳轉頁面等
定義一個異常處理類ExceptionEngine
public class ExceptionEngine {
public static final int UN_KNOWN_ERROR = 1000;//未知錯誤
public static final int ANALYTIC_SERVER_DATA_ERROR = 1001;//解析(服務器)數據錯誤
public static final int ANALYTIC_CLIENT_DATA_ERROR = 1002;//解析(客戶端)數據錯誤
public static final int CONNECT_ERROR = 1003;//網絡連接錯誤
public static final int TIME_OUT_ERROR = 1004;//網絡連接超時
public static ApiException handleException(Throwable e) {
ApiException ex;
if (e instanceof HttpException) { //HTTP錯誤
HttpException httpExc = (HttpException) e;
ex = new ApiException(e, httpExc.code());
ex.setMsg("網絡錯誤"); //均視爲網絡錯誤
return ex;
} else if (e instanceof ServerException) { //服務器返回的錯誤
ServerException serverExc = (ServerException) e;
ex = new ApiException(serverExc, serverExc.getCode());
ex.setMsg(serverExc.getMsg());
return ex;
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException || e instanceof MalformedJsonException) { //解析數據錯誤
ex = new ApiException(e, ANALYTIC_SERVER_DATA_ERROR);
ex.setMsg("解析錯誤");
return ex;
} else if (e instanceof ConnectException) {//連接網絡錯誤
ex = new ApiException(e, CONNECT_ERROR);
ex.setMsg("連接失敗");
return ex;
} else if (e instanceof SocketTimeoutException) {//網絡超時
ex = new ApiException(e, TIME_OUT_ERROR);
ex.setMsg("網絡超時");
return ex;
} else { //未知錯誤
ex = new ApiException(e, UN_KNOWN_ERROR);
ex.setMsg("未知錯誤");
return ex;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
異常處理類中,都是常見的錯誤類型,我們通過解析Throwable
轉換成統一的錯誤類ApiException
public class ApiException extends Exception {
private int code;//錯誤碼
private String msg;//錯誤信息
public ApiException(Throwable throwable, int code) {
super(throwable);
this.code = code;
}
public ApiException(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
這個類非常簡單,一個狀態碼,一個錯誤信息,方便我們開發調試。
不過仔細看代碼的同學會發現,ServerException
這個是什麼鬼?“服務器返回的錯誤”?Throwable
怎麼知道這個錯誤類型是ServerException
?
其實這個ServerException
是我們自定義的錯誤類型,一般我們開發中都會跟服務器約定一種接口請求返回的數據。比如:
- int code:表示接口請求狀態,0表示成功,-101表示密碼錯誤等等
- String msg:表示接口請求返回的描述。success,”token過期”等等
- Object result:成功是返回的數據
那麼我們就可以在解析服務端返回數據的時候,當code!=0,就拋出ServerException
public class ServerException extends RuntimeException {
private int code;
private String msg;
public ServerException(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
OK,到這裏我們常見的錯誤類型,異常等都處理完了,通過ExceptionEngine
轉化爲統一的錯誤類型ApiException
,在訂閱者回調onError(ApiException e)
就可以很方便知道錯誤的狀態碼以及對應的描述信息。
Observable observable = apiObservable
.map(new ServerResultFunction())
.onErrorResumeNext(new HttpResultFunction<>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
- 1
- 2
- 3
- 4
- 5
我們使用onErrorResumeNext(new HttpResultFunction<>())
操作符對Retrofit網絡請求拋出的Exception
進行處理,我們定義HttpResultFunction
處理Retrofit拋出的Exception
,通過ExceptionEngine
轉化爲統一的錯誤類型ApiException
public class HttpResultFunction<T> implements Function<Throwable, Observable<T>> {
@Override
public Observable<T> apply(@NonNull Throwable throwable) throws Exception {
//打印具體錯誤
LogUtils.e("HttpResultFunction:" + throwable);
return Observable.error(ExceptionEngine.handleException(throwable));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
這一步是對錯誤,異常等的處理,如果某一個http請求沒有發生異常,或者網絡錯誤,就會走onNext
回調。
前面我們約定,將服務器返回的邏輯錯誤也歸類到onError
。所以我們在.map(new ServerResultFunction())
操作符中處理服務器返回的結果是否正確(這裏指邏輯正確,即code==0),如果code!=0,就拋出ServerException
public class ServerResultFunction implements Function<HttpResponse, Object> {
@Override
public Object apply(@NonNull HttpResponse response) throws Exception {
//打印服務器回傳結果
LogUtils.e(response.toString());
if (!response.isSuccess()) {
throw new ServerException(response.getCode(), response.getMsg());
}
return new Gson().toJson(response.getResult());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
解析服務器返回結果 HttpResponse
,這個類由開發者自行設置,根據實際情況來定義字段,這裏我假設後臺返回的字段有
String msg; int retCode; Object result;
/**
* http響應參數實體類
* 通過Gson解析屬性名稱需要與服務器返回字段對應,或者使用註解@SerializedName
* 備註:這裏與服務器約定返回格式
*
* @author ZhongDaFeng
*/
public class HttpResponse {
/**
* 描述信息
*/
@SerializedName("msg")
private String msg;
/**
* 狀態碼
*/
@SerializedName("retCode")
private int code;
/**
* 數據對象[成功返回對象,失敗返回錯誤說明]
*/
@SerializedName("result")
private Object result;
/**
* 是否成功(這裏約定200)
*
* @return
*/
public boolean isSuccess() {
return code == 200 ? true : false;
}
public String toString() {
String response = "[http response]" + "{\"code\": " + code + ",\"msg\":" + msg + ",\"result\":" + new Gson().toJson(result) + "}";
return response;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
這裏Result
我們使用Object
因爲接口時通用的,服務端返回的接口類型也是多樣的,可能是列表
,也可能是JSON
對象,或者String
字符串,所以這裏我們使用Object,在數據解析的時候在轉化成爲具體的類型
嗯,錯誤處理搞定了,那就是簡單的封裝一下Retrofit 和RxJava以及使用RxLifecycle
RxJava使用訂閱模式,那我們就需要封裝一個被訂閱者,一個訂閱者,以及使用RxLifecycle自動管理訂閱的生命週期
構建Api接口類
public interface UserApi {
@GET("user/login")
Observable<HttpResponse> login(@QueryMap Map<String, Object> request);
}
- 1
- 2
- 3
- 4
- 5
- 6
構建Retrofit工具類獲取retrofit實例
public class RetrofitUtils {
/**
* 接口地址
*/
public static final String BASE_API = "http://apicloud.mob.com/";
public static final int CONNECT_TIME_OUT = 30;//連接超時時長x秒
public static final int READ_TIME_OUT = 30;//讀數據超時時長x秒
public static final int WRITE_TIME_OUT = 30;//寫數據接超時時長x秒
private static RetrofitUtils mInstance = null;
private RetrofitUtils() {
}
public static RetrofitUtils get() {
if (mInstance == null) {
synchronized (RetrofitUtils.class) {
if (mInstance == null) {
mInstance = new RetrofitUtils();
}
}
}
return mInstance;
}
/**
* 設置okHttp
*
* @author ZhongDaFeng
*/
private static OkHttpClient okHttpClient() {
//開啓Log
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
LogUtils.e("okHttp:" + message);
}
});
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(CONNECT_TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIME_OUT, TimeUnit.SECONDS)
.readTimeout(READ_TIME_OUT, TimeUnit.SECONDS)
.addInterceptor(logging)
.build();
return client;
}
/**
* 獲取Retrofit
*
* @author ZhongDaFeng
*/
public Retrofit retrofit() {
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient())
.baseUrl(BASE_API)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return retrofit;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
構建網絡請求(被訂閱對象)
/**
* 適用Retrofit網絡請求Observable(被監聽者)
*
* @author ZhongDaFeng
*/
public class HttpRxObservable {
/**
* 獲取被監聽者
* 備註:網絡請求Observable構建
* data:網絡請求參數
* <h1>補充說明</h1>
* 傳入LifecycleProvider自動管理生命週期,避免內存溢出
* 備註:需要繼承RxActivity.../RxFragment...
*
* @author ZhongDaFeng
*/
public static Observable getObservable(Observable<HttpResponse> apiObservable, LifecycleProvider lifecycle) {
//showLog(request);
Observable observable;
//隨生命週期自動管理.eg:onCreate(start)->onStop(end)
observable =apiObservable
.map(new ServerResultFunction())
.compose(lifecycle.bindToLifecycle())//需要在這個位置添加
.onErrorResumeNext(new HttpResultFunction<>())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
return observable;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
在HttpRxObservable
我們構建一個被訂閱者Observable
並且設置了錯誤處理,同時添加了生命週期管理。
處理訂閱者Observer
/**
* 適用Retrofit網絡請求Observer(監聽者)
* 備註:
* 1.重寫onSubscribe,添加請求標識
* 2.重寫onError,封裝錯誤/異常處理,移除請求
* 3.重寫onNext,移除請求
* 4.重寫cancel,取消請求
*
* @author ZhongDaFeng
*/
public abstract class HttpRxObserver<T> implements Observer<T>, HttpRequestListener {
private String mTag;//請求標識
public HttpRxObserver() {
}
public HttpRxObserver(String tag) {
this.mTag = tag;
}
@Override
public void onError(Throwable e) {
RxActionManagerImpl.getInstance().remove(mTag);
if (e instanceof ApiException) {
onError((ApiException) e);
} else {
onError(new ApiException(e, ExceptionEngine.UN_KNOWN_ERROR));
}
}
@Override
public void onComplete() {
}
@Override
public void onNext(@NonNull T t) {
if (!TextUtils.isEmpty(mTag)) {
RxActionManagerImpl.getInstance().remove(mTag);
}
onSuccess(t);
}
@Override
public void onSubscribe(@NonNull Disposable d) {
if (!TextUtils.isEmpty(mTag)) {
RxActionManagerImpl.getInstance().add(mTag, d);
}
onStart(d);
}
@Override
public void cancel() {
if (!TextUtils.isEmpty(mTag)) {
RxActionManagerImpl.getInstance().cancel(mTag);
}
}
/**
* 是否已經處理
*
* @author ZhongDaFeng
*/
public boolean isDisposed() {
if (TextUtils.isEmpty(mTag)) {
return true;
}
return RxActionManagerImpl.getInstance().isDisposed(mTag);
}
protected abstract void onStart(Disposable d);
/**
* 錯誤/異常回調
*
* @author ZhongDaFeng
*/
protected abstract void onError(ApiException e);
/**
* 成功回調
*
* @author ZhongDaFeng
*/
protected abstract void onSuccess(T response);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
使用網絡請求的過程中我們肯定會遇到取消請求的場景,這裏我們實現一個HttpRequestListener
,爲每一個請求添加唯一的TAG
用來標識具體的每一個請求,開始請求時保存TAG,請求成功/失敗移除標誌,同時TAG也用做取消請求的標誌。
編寫一個測試類,示例如何使用Retrofit
/**
* Retrofit使用demo/測試類
*
* @author ZhongDaFeng
*/
public class RetrofitTest {
public final String TAG = RetrofitTest.class.getSimpleName();//每個網絡請求唯一TAG,用於取消網絡請求使用
/**
* 模擬在activity中調用
*
* @author ZhongDaFeng
*/
public void test(RxActivity activity, String account, String psw) {
//設置唯一TAG
HttpRxObserver httpRxObserver = new HttpRxObserver(TAG + "login") {
@Override
protected void onStart(Disposable d) {
/**
* 開啓loading等
*/
}
@Override
protected void onError(ApiException e) {
/**
* 錯誤信息
*/
LogUtils.w("onError code:" + e.getCode() + " msg:" + e.getMsg());
}
@Override
protected void onSuccess(Object response) {
/**
* 成功處理
*/
LogUtils.w("onSuccess response:" + response.toString());
}
};
new RetrofitTest().login(activity, account, psw).subscribe(httpRxObserver);
//取消請求
/*if(!httpRxObserver.isDisposed()){
httpRxObserver.cancel();
}*/
}
/**
* 登錄demo
*
* @author ZhongDaFeng
*/
public Observable login(RxActivity activity, String phone, String psw) {
//構建請求數據
Map<String, Object> request = HttpRequest.getRequest();
request.put("phone", phone);
request.put("psw", psw);
/**
* 獲取請求Observable
* 1.RxActivity,RxFragment...所在頁面繼承RxLifecycle支持的組件
* 2.ActivityEvent指定監聽函數解綁的生命週期(手動管理,未設置則自動管理)
* 以上兩點作用防止RxJava監聽沒解除導致內存泄漏,ActivityEvent若未指定則按照activity/fragment的生命週期
*/
// return HttpRxObservable.getObservable(ApiUtils.getPhoneApi().phoneQuery(request), activity);
return HttpRxObservable.getObservable(ApiUtils.getUserApi().login(request), activity, ActivityEvent.PAUSE);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
使用RxLifecycle
管理訂閱生命週期activity需要繼承RxActivity
。開篇也提到MVP的時候,接下來就說說在MVP中如何使用。
定義一個Presenter基類
public class BasePresenter<V, T> implements LifeCycleListener {
protected Reference<V> mViewRef;
protected V mView;
protected Reference<T> mActivityRef;
protected T mActivity;
public BasePresenter(V view, T activity) {
attachView(view);
attachActivity(activity);
setListener(activity);
}
/**
* 設置生命週期監聽
*
* @author ZhongDaFeng
*/
private void setListener(T activity) {
if (getActivity() != null) {
if (activity instanceof BaseActivity) {
((BaseActivity) getActivity()).setOnLifeCycleListener(this);
} else if (activity instanceof BaseFragmentActivity) {
((BaseFragmentActivity) getActivity()).setOnLifeCycleListener(this);
}
}
}
/**
* 關聯
*
* @param view
*/
private void attachView(V view) {
mViewRef = new WeakReference<V>(view);
mView = mViewRef.get();
}
/**
* 關聯
*
* @param activity
*/
private void attachActivity(T activity) {
mActivityRef = new WeakReference<T>(activity);
mActivity = mActivityRef.get();
}
/**
* 銷燬
*/
private void detachView() {
if (isViewAttached()) {
mViewRef.clear();
mViewRef = null;
}
}
/**
* 銷燬
*/
private void detachActivity() {
if (isActivityAttached()) {
mActivityRef.clear();
mActivityRef = null;
}
}
/**
* 獲取
*
* @return
*/
public V getView() {
if (mViewRef == null) {
return null;
}
return mViewRef.get();
}
/**
* 獲取
*
* @return
*/
public T getActivity() {
if (mActivityRef == null) {
return null;
}
return mActivityRef.get();
}
/**
* 是否已經關聯
*
* @return
*/
public boolean isViewAttached() {
return mViewRef != null && mViewRef.get() != null;
}
/**
* 是否已經關聯
*
* @return
*/
public boolean isActivityAttached() {
return mActivityRef != null && mActivityRef.get() != null;
}
@Override
public void onCreate(Bundle savedInstanceState) {
}
@Override
public void onStart() {
}
@Override
public void onRestart() {
}
@Override
public void onResume() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void onDestroy() {
detachView();
detachActivity();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
JAVA弱引用,管理View的引用,以及activity的引用,避免強引用導致資源無法釋放而造成的內存溢出,
寫代碼的時候想到了一個很巧妙的方式:Presenter中傳如一個activity,同時實現Activity生命週期監聽,在onDestroy中移除View和Activity的引用,個人覺得這個非常不錯。看官可以客觀評價一下優劣。
登錄Presenter
public class LoginPresenter extends BasePresenter<ILoginView, LoginActivity> {
private final String TAG = PhoneAddressPresenter.class.getSimpleName();
public LoginPresenter(ILoginView view, LoginActivity activity) {
super(view, activity);
}
/**
* 登錄
*
* @author ZhongDaFeng
*/
public void login(String userName, String password) {
//構建請求數據
Map<String, Object> request = HttpRequest.getRequest();
request.put("username", userName);
request.put("password", password);
HttpRxObserver httpRxObserver = new HttpRxObserver(TAG + "getInfo") {
@Override
protected void onStart(Disposable d) {
if (getView() != null)
getView().showLoading();
}
@Override
protected void onError(ApiException e) {
LogUtils.w("onError code:" + e.getCode() + " msg:" + e.getMsg());
if (getView() != null) {
getView().closeLoading();
getView().showToast(e.getMsg());
}
}
@Override
protected void onSuccess(Object response) {
LogUtils.w("onSuccess response:" + response.toString());
UserBean bean = new Gson().fromJson(response.toString(), UserBean.class);
if (getView() != null) {
getView().closeLoading();
getView().showResult(bean);
}
}
};
/**
* 切入後臺移除RxJava監聽
* ActivityEvent.PAUSE(FragmentEvent.PAUSE)
* 手動管理移除RxJava監聽,如果不設置此參數默認自動管理移除RxJava監聽(onCrete創建,onDestroy移除)
*/
HttpRxObservable.getObservable(ApiUtils.getUserApi().login(request), getActivity(), ActivityEvent.PAUSE).subscribe(httpRxObserver);
/**
* ******此處代碼爲了測試取消請求,不是規範代碼*****
*/
/*try {
Thread.sleep(50);
//取消請求
if (!httpRxObserver.isDisposed()) {
httpRxObserver.cancel();
}
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
LoginActivity
public class LoginActivity extends BaseActivity implements ILoginView {
@BindView(R.id.et_user_name)
EditText etUserName;
@BindView(R.id.et_password)
EditText etPassword;
private LoginPresenter mLoginPresenter = new LoginPresenter(this, this);
private RLoadingDialog mLoadingDialog;
@Override
protected int getContentViewId() {
return R.layout.activity_login;
}
@Override
protected void init() {
mLoadingDialog = new RLoadingDialog(this, true);
}
@Override
protected void initBundleData() {
}
@OnClick({R.id.login})
public void onClick(View v) {
switch (v.getId()) {
case R.id.login:
String userName = etUserName.getText().toString();
String password = etPassword.getText().toString();
if (TextUtils.isEmpty(userName) || TextUtils.isEmpty(password)) {
return;
}
mLoginPresenter.login(userName, password);
break;
}
}
@Override
public void showResult(UserBean bean) {
if (bean == null) {
return;
}
showToast(bean.getUid());
}
@Override
public void showLoading() {
mLoadingDialog.show();
}
@Override
public void closeLoading() {
mLoadingDialog.dismiss();
}
@Override
public void showToast(String msg) {
ToastUtils.showToast(mContext, msg);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
以上是我對MVP的一點理解,以及對RxJava2+Retrofit2+RxLifecycle2進行的封裝,由於編寫demo的時候是朝着構建一個項目框架走的,所以沒有貼完的代碼就請移步到我的Github克隆完整版本,盡情的擼吧。
個人認爲這個搭配還是可行的,並且已經投入都項目中。好產品一定要自己先使用,認可。哈哈哈
https://github.com/RuffianZhong/Rx-Mvp
- 1
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-778f64ae39.css" rel="stylesheet">
</div>
轉自:https://blog.csdn.net/u014702653/article/details/75268919?utm_source=blogxgwz1