Retrofit結合RxJava的一次實踐

公司的一個項目中已經採用了Retrofit加RxJava的作爲網絡請求框架,強大的框架所以替換了那麼久決定記錄一下我的實踐過程。

第一步,觀察服務器返回的結果定義返回結構實體類

{
    "errno": 0,
    "errmsg": "操作成功",
    "data": []
}

所以我們可以定義個NetResponse< T >來作爲一次服務器返回結果實體類其中的T是對應返回json中的data字段,可以是一個對象或者是一個列表結果,所以定義一個實體類出來。

public class NetResponse<T> {
    private String errno;
    private String errmsg;
    private T data;
}

第二步,初始化Retrofit
我們可以查看一下Square.Retrofit 官方對應的文檔介紹,首先我們需要一個interface來存放接口請求地址,那麼我們跟着文檔的步驟來

public interface UserService {

    /**
     * 登錄
     */
    @FormUrlEncoded
    @POST("/login/")
    Observable<NetResponse<UserInfo>> doLogin(@Field("account") String account, @Field("pwd") String password);
}

這裏我們簡單的舉例爲用戶登錄的過程,因爲這是一個post請求所以按文檔需求我們使用了FormUrlEncoded和Field註釋字段。那如果是get請求呢?這裏我也舉個例子來表示get請求<這個例子是Retrofit的官方文檔例子>

@GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);

這樣我們就懂了 如何使用Retrofit來實現get/post請求操作了。定義完了請求interface後我們該下一步初始化Retrofit了,因爲我項目中採用的是Fastjson作爲json解析工具,這裏需要先封裝一個FastConverterFactory,爲什麼呢?因爲Retrofit支持自定義Json解析類封裝類似自帶的Gson解析:

addConverterFactory(GsonConverterFactory.create())

所以我們也自己定義一個ConverterFactory來作爲FastJson解析類。

final class FastResponseBodyConverter<T> implements Converter<ResponseBody, T> {

    private Type type;

    public FastResponseBodyConverter(Type type) {
        this.type = type;
    }

    @Override
    public T convert(ResponseBody value) throws IOException {
        BufferedSource bufferedSource = Okio.buffer(value.source());
        String tempStr = bufferedSource.readUtf8();
        bufferedSource.close();
        LogUtil.w("FastResponseBodyConverter", "-- FastJsonRequestResponse = " + tempStr);
        return JSON.parseObject(tempStr, type);
    }
}

定義一個FastResponseBodyConverter來將json解析過程寫在convert方法中

public class FastConverterFactory extends Converter.Factory {

    public static FastConverterFactory create() {
        return new FastConverterFactory();
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
        return new FastResponseBodyConverter<>(type);
    }
}

再定義一個FastConverterFactory來初始化Fastjson解析,這樣我們使用過程中不需要關心解析過程了,直接可以衝NetResponse中getT()方法得到我們想要的結果。
所以Retrofit的初始化過程我把定義了一個獨有的類。

public class AppRetrofit extends BaseRetrofit {

    private AppService mService;
    private Retrofit mRetrofit;

    public AppService getService() {
        if (null == mService) {
            mService = getRetrofit().create(AppService.class);
        }
        return mService;
    }

    public Retrofit getRetrofit() {
        if (null == mRetrofit) {
            mRetrofit = new Retrofit.Builder()
                    .client(getOKHttpClient())
                    .baseUrl(BaseRetrofit.APP_URL)
                    .addConverterFactory(FastConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .build();
        }
        return mRetrofit;
    }
}

需要注意的是一定要加入下面這句代碼才能支持RxJava。

addCallAdapterFactory(RxJavaCallAdapterFactory.create())

其中的BaseRetrofit的完成類如下:

public class BaseRetrofit {

    private static final String TAG = "BaseRetrofit";

    /**
     * http請求成功
     */
    public static final String HTTP_SUCCESS = "0";

    public static final String USER_URL = "app_server_url";

    /**
     * 返回OkHttp配置
     */
    public OkHttpClient getOKHttpClient() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                LogUtil.e("Retrofit", message);
            }
        });
        HttpHeaderInterceptor headerInterceptor = new HttpHeaderInterceptor();
        return new okhttp3.OkHttpClient.Builder()
                .addNetworkInterceptor(interceptor)
                .addNetworkInterceptor(headerInterceptor)
                .build();
    }

    /**
     * 請求結果轉換
     */
    public final Observable.Transformer mTransformer = new Observable.Transformer() {
        @Override
        public Object call(Object observable) {
            return ((Observable) observable)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .flatMap(new Func1() {
                        @Override
                        public Object call(Object response) {
                            return _flatResponse((NetResponse<Object>) response);
                        }
                    });
        }
    };

    /**
     * 將返回的數據結果直接轉成想要的數據
     */
    public final <T> Observable<T> _flatResponse(final NetResponse<T> response) {
        return Observable.create(new Observable.OnSubscribe<T>() {
            @Override
            public void call(Subscriber<? super T> subscriber) {
                if (!subscriber.isUnsubscribed()) {//  如果已經被回收的Subscribe的話就直接跳過
                    if (HTTP_SUCCESS.equals(response.getErrno())) {// 請求成功...
                        subscriber.onNext(response.getData());
                        subscriber.onCompleted();
                    } else {
                        LogUtil.e(TAG, "--- Retrofit request error ..........");
                        subscriber.onError(new ApiException(response.getErrno(), response.getErrmsg()));
                    }
                }
            }
        });
    }
}

其中我們還是需要解釋一下Observable.Transformer對象的mTransformer,他的作用就是代碼複用功效作用就是將我們返回的結果Observable< NetResponse< T > >轉換成Observable跟操作符flatmap的效果類似,不同的是Transformer可以一開始就執行而不像flatmap需要等subscribe之後才調用,所以轉換的話還是推薦使用Transformer類來解決,可以參考這篇文章避免打斷鏈式結構:使用.compose( )操作符

第三步,調用。

public class UserNetRetrofit extends UserRetrofit {

    /**
     * 登錄
     */
    public Observable<UserInfo> doLogin(String account, String password) {
        return getService().doLogin(account, password).compose(mTransformer);
    }
}

我們這裏再一次把定義一個可見的類,把一切操作都寫在這裏面並且返回一個個Observable< T >給我們需要的View層自行拿取就好了。
所以代碼的調用如下:

final UserNetRetrofit loginRetrofit = new UserNetRetrofit();
        Subscription subscription = loginRetrofit.doLogin(name, pwd)
                .subscribe(new Action1<UserInfo>() {
                    @Override
                    public void call(UserInfo info) {
                        // 拿到UserInfo對象自行操作
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable e) {
                        // 一次請求操作失敗處理
                    }
                });
        BaseRetrofit.addSubscription(context, subscription);

如果你項目中採用的MVP模式可以把這段代碼寫到presenter層中,然後在call方法中分別回調成功或者失敗到view層,代碼看起來比較解耦不會很雜,ps:這裏並沒有那麼處理。

最後,注意點:
是不是發現了addSubscription這是什麼方法,Retrofit並沒有提供這個東西。對,這是自己寫的。

    /**
     * 存放Retrofit請求
     */
    public CompositeSubscription mCompositeSubscription;

    public void addSubscription(Subscription subscription) {
        if (null != mCompositeSubscription) {
            mCompositeSubscription.add(subscription);
        }
    }

就是這麼個東西,定義一個CompositeSubscription對象用於存放每一次的請求,爲了就是怕請求還沒成功的時候界面finish後未能釋放RxJava操作導致的內存泄露威脅。

    @Override
    protected void onDestroy() {
        //關閉界面上的請求
        if (null != mCompositeSubscription) {
            mCompositeSubscription.unsubscribe();
        }
        super.onDestroy();
    }

這樣一次完整的請求封裝就好了, 可以把一些公用的方法抽取放到BaseFragment或者BaseActivity中這樣就不用寫多餘的代碼。

發佈了79 篇原創文章 · 獲贊 105 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章