RxJava2+Retrofit2+RxLifecycle2使用MVP模式構建項目

前言

眼下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克隆完整版本,盡情的擼吧。
個人認爲這個搭配還是可行的,並且已經投入都項目中。好產品一定要自己先使用,認可。哈哈哈

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章