煮 Retrofit 論 RxJava(一)

首先,本篇是基于這個的總結、思考和拓展,那篇作者由淺入深,徐徐道來,讀起來感覺很棒。

** 1、使用Retrofit來進行網絡請求 **

a、網絡接口使用豆瓣電影的top250,我們根據它返回的json格式封裝為一個MovieEntity。

https://api.douban.com/v2/movie/top250?start=0&count=10

b、然后創建接口MovieService:

public interface MovieService {
    /** 以Get方法訪問接口,參數start和count分別是對接口地址的傳參 **/
    @GET("top250")
    Call<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
}

c、接著,在界面中使用Retrofit進行網絡請求:

private void getMovie() {
        String baseUrl = "https://api.douban.com/v2/movie/";
        /** 創建Retrofit對象,數據轉換使用Gson **/
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        /** 通過MoviceService得到Call,然后加入請求隊列并,并在Callback接口方法中處理返回結果 **/
        MovieService movieService = retrofit.create(MovieService.class);
        Call<MovieEntity> call = movieService.getTopMovie(0, 10);
        call.enqueue(new Callback<MovieEntity>() {
            @Override
            public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) {
                resultTV.setText(response.body().toString());
            }

            @Override
            public void onFailure(Call<MovieEntity> call, Throwable t) {
                resultTV.setText(t.getMessage());
            }
        });
    }

是的,以上方法便可以完成請求,但是看起來不美,我們可以通過封裝請求方法,在Activity或Fragment中只保留更新UI相關的邏輯。OK,單純的封裝比較簡單,我們一并加入Rxjava來看看。

** 2、添加Rxjava并對請求過程進行封裝 **

a、我們之前定義的Service返回值就由Call變為Observable了。

public interface MovieService {
    /** 返回的Observable正是被觀察者,我們用來通知觀察者對象(這里我們通知UI更新) **/
    @GET("top250")
    Observable<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
}

b、把請求的過程封裝到HttpMethods類里面,

public class HttpMethods {
    public static final String BASE_URL = "https://api.douban.com/v2/movie/";
    private static final int DEFAULT_TIMEOUT = 5;

    private Retrofit retrofit;
    private MovieService movieService;

    //構造方法私有
    private HttpMethods() {
        //手動創建一個OkHttpClient并設置超時時間
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);

        retrofit = new Retrofit.Builder()
                .client(httpClientBuilder.build())
                .addConverterFactory(GsonConverterFactory.create())
                //此處添加RxJavaCallAdapterFactory,把請求結果直接映射為
                //MovieService接口方法返回的具體類型MovieEntity
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();

        movieService = retrofit.create(MovieService.class);
    }

    //在訪問HttpMethods時創建單例
    private static class SingletonHolder{
        private static final HttpMethods INSTANCE = new HttpMethods();
    }

    //獲取單例
    public static HttpMethods getInstance(){
        return SingletonHolder.INSTANCE;
    }

    /**
     * 用于獲取豆瓣電影Top250的數據
     * @param subscriber  由調用者傳過來的觀察者對象
     * @param start 起始位置
     * @param count 獲取長度
     */
    public void getTopMovie(Subscriber<MovieEntity> subscriber, int start, int count){
        movieService.getTopMovie(start, count)
                //指定subscribe()發生在io調度器(讀寫文件、讀寫數據庫、網絡信息交互等)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                //指定subscriber的回調發生在主線程
                .observeOn(AndroidSchedulers.mainThread())
                //實現訂閱關系
                .subscribe(subscriber);
    }
}

c、那么getMovie()方法直接調用封裝過的getTopMovie()即可。從中,我們看到使用Rxjava帶來兩點變化:一個是將請求封裝進Observable;另外是通過實現Subscriber中的三個接口方法來通知請求狀態(即UI更新)。

   private void getMovie() {
        subscriber = new Subscriber<MovieEntity>() {
            @Override
            public void onCompleted() {
                Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onError(Throwable e) {
                resultTV.setText(e.getMessage());
            }

            @Override
            public void onNext(MovieEntity movieEntity) {
                /** 這里得到的請求結果直接是我們想要的java對象 **/
                resultTV.setText(movieEntity.toString());
            }
        };
        HttpMethods.getInstance().getTopMovie(subscriber, 0, 10);
    }

從上面我們知道Retrofit會把返回的json對象映射為我們需要的java對象,我們的應用中肯定會用到很多網絡接口,我們現在是請求的電影列表,我們還會請求電影詳情,演員詳情等等,那我們就需要定義MovieDetail、Actor等等Entity。問題是我們的服務通常會給我們返回一個固定樣式的數據,形如:

{
 "resultCode": 1,
 "resultMsg": "成功",
 "data": {}
}

而且我們通常會先去判斷resultCode,以便可以統一處理錯誤信息。至于data中的結構,則是變化的,它可以是電影列表Movies,也可以是演員信息Actor,那么此時我們就會想起使用泛型來進行封裝:

public class HttpResult<T> {
    private int resultCode;
    private String resultMsg;
    private T data;
}

如果data是Actor對象的話,那么定義Service方法返回值就可以寫成:

observable<HttpResult<Actor>>

如果data是電影列表,那么我們可以這樣寫:

observable<HttpResult<List<Movie>>>

考慮到一般列表的數據都會有分頁信息,我們仍然可以繼續封裝:

/** 支持分頁的json結構 **/
{
 "start": 0,
 "count": 20,
 "total": 250
 "list": []
}
/** 封裝后帶分頁的泛型對象 **/
public class HttpResList<T> {
    private int start;
    private int count;
    private int total;
    private T list;
}

那么電影列表,就需要這么寫了:

observable<HttpResult<HttpResList<List<Movie>>>>

啊啊啊,是不是有一種碟中諜、局中局的感覺…當然,為了減少層級,我們可以把分頁的相關信息提到resultCode這個層級來,這個需要我們跟服務端協定好一個規則。

那么封裝為泛型后的代碼跟上面的相差不大,只是把Observable相關的參數或返回類型由MovieEntity改為HttpResult<List<Movie>>(注意,我們所使用的測試接口跟我們以上討論的結構有出入),所有就不再貼出代碼。

所以,協定好一個規范、好用的數據結構,是多么的酸爽。

其實,我一直有個疑惑,為什么要用Rxjava呢?沒看到代碼簡潔、好用啊,是啊,Why?請移步 煮 Retrofit 論 RxJava(二)

轉載請標明出處:http://www.lxweimin.com/p/86cf7de684b3

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容