首先,本篇是基于這個的總結、思考和拓展,那篇作者由淺入深,徐徐道來,讀起來感覺很棒。
** 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(二)。