Retrofit2.0起步篇


retrofit 英文名字是改裝的意思,也就是說他是對網絡請求的一種改裝,他不負責進行網絡請求,他是對請求方式的一種封裝。真正進行網絡請求的是okhttp。
以下所有內容在Android Studio已經導入retrofit為基礎。導入方式如下:

  compile 'com.squareup.retrofit2:retrofit:2.1.0'
  compile 'com.squareup.retrofit2:converter-gson:2.1.0'
  compile 'com.squareup.retrofit2:converter-scalars:2.1.0'

利用Retrofit進行簡單的GET請求

retrofit在構建請求方式之前,需要構建一個接口方法,通過這個接口方法的返回值,來進行網絡請求。
下面,來通過一些簡單的例子了解GET請求。

實驗一:對一個簡單的html頁面進行GET請求

我們要獲取百度頁面的HTML。首先構建如下接口:

public interface HtmlService {
    @GET("/")
    Call<String> getHtml();
}

注意,GET注解中的參數,和方法中的參數至少要加一個,否則會報錯。由於,我們只需要請求www.baidu.com,所以get這里不需要加參數,就是/
然后,我們通過如下步驟,來進行網絡請求。
在我們需要進行網絡請求的類中,通過以下的步驟,進行網絡請求:

  1. 構建retrofit對象。
  2. 動態代理生成接口的對象。
  3. 通過接口的方法,得到要請求的API的調用。
  4. 通過同步/異步的方式,得到response。
  5. 根據需要,處理response。

第一步

Retrofit retrofit = new Retrofit.Builder().          addConverterFactory(ScalarsConverterFactory.create()).
                baseUrl("https://www.baidu.com").
                build();

通過以上代碼,可以簡單的構建一個retrofit對象,addConverterFactory是對response進行解析,里面添加的參數是表示對response用String解析,然后添加一個基礎的URL,后續的參數則是通過上面我們定制的接口來添加,最后構建一個完整的URL。
第二步

HtmlService htmlService = retrofit.create(HtmlService.class);

通過動態代理,生成一個接口的對象。

第三步

Call<String> call = htmlService.getHtml();

通過接口的方法得到調用的對象。

第四步與第五步
異步方法得到response:

call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                showText.append(response.body());
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {
                Toast.makeText(MainActivity.this,t.getMessage(),Toast.LENGTH_SHORT).show();
            }
        });

得到的response,通過response.body()得到響應報文的body部分。
同步方法得到response:

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    final String str = call.execute().body();
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            showText.append(str);
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

實驗二:對一個返回JSON格式的API進行GET請求

通過GET請求GankIO的api得到Json:
首先,我們也是通過接口,構建一個接口方法:

@GET("content/{number}/{page}")
Call<HistoryBean> getHistoryData(@Path("number") String number,@Path("page") String page);

這里,方法里面傳入的參數會放到@GET的注解里面。
然后,重新構建一個retrofit對象:

Retrofit retrofit = new Retrofit.Builder().
              addConverterFactory(GsonConverterFactory.create()).
                baseUrl("http://gank.io/api/history/").
                build();

這里面添加的解析器是GsonConverterFactory,表示對response中body提供對象解析。然后的方法和上面類似:

 HtmlService htmlService = retrofit.create(HtmlService.class);
        call = htmlService.getHistoryData("2", "1");
call.enqueue(new Callback<HistoryBean>() {
            @Override
            public void onResponse(Call<HistoryBean> call, Response<HistoryBean> response) {
                HistoryBean hb = response.body();
                if(hb == null) return;
                showText.append(hb.isError() + "");
                for(HistoryBean.ResultsBean rb : hb.getResults()){
                    showText.append(rb.getTitle() + "/n");
                }
            }

            @Override
            public void onFailure(Call<HistoryBean> call, Throwable t) {

            }
        });

上面的方法是異步得到的,同步的方法和上面類似,就不多說了。

實驗三:添加一個請求參數構建GET請求

上面的GET方法是沒有查詢參數的,下面對一個有查詢參數的api,進行GET請求,這里我們利用豆瓣的搜索圖書的API
這個API接受4個搜索參數,具體如下:

參數 意義 備注
q 查詢關鍵字 q與tag必傳其一
tag 查詢的tag q與tag必傳其一
start 取結果的offset 默認為0
count 取結果的條數 默認為20

首先,我們也是構建一個請求接口的方法:

@GET("book/search")
Call<BookBean> queryBookInfo(@Query("q") String name);

在這里面,我們用到了一個新的注解參數@Query 這個參數表示請求參數會以鍵值對的形式拼接在URL后面。
這樣的方式,有一種局限性,因為要在每個GET接口方法里面寫入鍵值對信息,如果,有些鍵值對信息是每個GET方法都需要的,我們就會通過一個攔截器的形式,統一在請求的時候加上。步驟如下:

  1. 自定義一個攔截器實現Interceptor
  2. 創建retrofit的客戶端(上面的代碼都是默認的客戶端),加上這個攔截器。

第一步

public class CustomInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        HttpUrl httpUrl = request.url().newBuilder()
                .addQueryParameter("token", "tokenValue")
                .build();
        request = request.newBuilder().url(httpUrl).build();
        return chain.proceed(request);
    }
}

第二步

OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder().
                addInterceptor(new CustomInterceptor()).
                connectTimeout(1000, TimeUnit.MILLISECONDS);
        
Retrofit retrofit = new Retrofit.Builder().
                client(httpClientBuilder.build()).
                addConverterFactory(GsonConverterFactory.create()).
                baseUrl("https://api.douban.com/v2/").
                build();
HtmlService htmlService = retrofit.create(HtmlService.class);
call = htmlService.queryBookInfo("第一行代碼");

后續的異步請求基本一致,就不細說了。

實驗四:添加多種請求參數構建GET請求

實驗三的部分,講了對一個查詢參數和一個共有的查詢參數的GET請求構建方法,下面多個查詢參數的GET請求,看看是否有簡單的方式,因為不想在一個方法里,傳入4個以上的參數。
請求的API還是上邊的豆瓣的搜索API,他正好有4個請求參數
下面,看如下構建方式:

@GET("book/search")
Call<BookBean> queryBookInfo(@QueryMap Map<String,String> options);

然后,將請求參數通過鍵值對的形式保存到Map里:

 Map<String,String> options = new HashMap<>();
 options.put("q","第一行代碼");
 options.put("start","0");
 options.put("count","1");
 call = htmlService.queryBookInfo(options);

在上面的情況下,有多種鍵值對,每一種key對應的value都是唯一的,retrofit也支持相同的key,卻有多種value的形式。方式如下:

@GET("book/search")
Call<BookBean> queryBookInfo(@Query("key") List<String> value);

然后,將value的集合傳入方法中,后續的步驟不變,就不多數。

利用Retrofit進行簡單的POST請求

利用retorfit進行post請求與進行get請求沒有太多的區別。主要的區別就在構建接口方法上面,有一些不同,下面通過一些實驗來看一下具體的區別。

實驗一:將少數參數放到請求體中進行POST請求

下面的POST方法API是我自己寫的后台來接受簡單的POST,就不放出來了。
首先,也是構建一個接口方法:

@FormUrlEncoded
@POST("login")
Call<String> doLogin(@Field("name")String name,@Field("password") String password);

第一個注解,表示自動將請求參數的類型調整為application/x-www-form-urlencoded ,如果方法參數的注解用了@Field 就一定要用@FormUrlEncoded。POST注解里面依舊放的是API,方法參數通過@Field將請求參數放置在請求體中。
后續創建retrofit對象,創建call對象,發起請求,都是和GET方法一樣,就不多說了。

實驗二:將多個參數放到請求體中進行POST請求

這個只不過構建接口方法的時候,有所區別,其他步驟和多種參數進行GET請求一樣。

@FormUrlEncoded
@POST("login")
Call<String> doLogin(@FieldMap Map<String,String> fields);

將多個請求參數保存到Map中,傳入方法中,沒什么好說的。

實驗三:利用POST進行文件上傳

同樣構建一個接口方法:

@Multipart
@POST("upload")
Call<ResponseBody> uploadFile(@Part("description") RequestBody description,
@Part MultipartBody.Part file);

這里的POST修飾注解就換成了@Multipart ,在方法參數里面,通過@Part注解來修飾參數。
我們說一下具體怎么構建這些方法的參數。
首先,構建一個RequestBody對象的description,構建方式如下:

RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), "這是一個文件");

然后,構建一個MultipartBody.Part對象的file,不過在構建之前,先要創建一個RequestBody對象,通過這個對象才能創建一個MultipartBody.Part對象。代碼如下:

RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"),file);
MultipartBody.Part body = MultipartBody.Part.createFormData("file",file.getName(),requestBody);

然后和其他方法一樣,利用這些對象,生成call,然后進行網絡請求。

 call = service.uploadFile(description,body);

當然,retrofit支持多種上傳圖片的方式,其構建方式如下:

// 上傳多個文件
    @Multipart
    @POST("upload")
    Call<ResponseBody> uploadMultipleFiles(
            @Part("description") RequestBody description,
            @Part MultipartBody.Part file1,
            @Part MultipartBody.Part file2);

以上這些實驗就是利用Retrofit實現簡單POST 請求。

添加HEADER

我們在進行復雜的網絡請求的時候,通常要對請求添加頭部,retrofit提供了多種添加方式,可以分為如下兩種:

  1. 靜態添加頭部
  2. 動態添加頭部

靜態添加頭部

靜態添加頭部,則每次請求的時候,頭部的信息都是固定的,不可以更改的。添加靜態頭部的方式也有多種,方法如下:

  1. 在接口中添加。
  2. 通過攔截器添加。

下面,我們分別說一說每一種。
在接口中添加
還是以上文寫到的接口為例,添加Cache-Control,User-Agent請求頭部。

@Headers({
            "Cache-Control: max-age=640000",
            "User-Agent: app-name"
    })

通過攔截器添加
和上面統一處理GET參數的定制器一樣,同樣實現Interceptor,代碼如下:

public class RequestInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request original = chain.request();
        Request request = original.newBuilder()
                .header("User-Agent", "app-name")
                .header("Cache-Control", "max-age=640000")
                .method(original.method(), original.body())
                .build();
        return chain.proceed(request);
    }
}

然后,在創建okHttp的客戶端時,把攔截器加進去,創建retrofit對象時,指定該客戶端即可。

  OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder().
                addInterceptor(new CustomInterceptor()).
                addInterceptor(new RequestInterceptor()).
                connectTimeout(1000, TimeUnit.MILLISECONDS);

動態添加頭部

動態添加的好處,就在於不同的請求會有不同的請求頭部,那么可想而知,其添加的部分就是在接口方法里面。

@GET("{number}/{page}")
Call<HistoryBean> getHistoryData(@Header("Content-Range") String contentRange ,@Path("number") String number, @Path("page") String page);

后續的使用都是一樣的,沒什么好說的。

設置網絡請求日志

retrofit提供了對網絡請求的過程進行打印的日志的插件,需要單獨的導入,其方式如下:

compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'

通過上面的導入代碼,可以看出這個日志插件是okHttp特有的,這也可以證明,retrofit只是封裝了請求的方式,真正請求的還是通過okHttp。那么我們也可以猜出,其設置的方式必然也是通過攔截器,放進okHttp的客戶端里面。具體的代碼如下:

HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder().
                addInterceptor(new CustomInterceptor()).
                addInterceptor(new RequestInterceptor()).
                addInterceptor(httpLoggingInterceptor).
                connectTimeout(1000, TimeUnit.MILLISECONDS);

注意上面的代碼,在設置請求級別上面設置為body,下面說一下具體的級別以及其內涵:

  1. NONE : 沒有任何日志信息。
  2. Basic : 打印請求類型,URL,請求體大小,返回值狀態以及返回值的大小。
  3. Headers : 打印返回請求和返回值的頭部信息,請求類型,URL以及返回值狀態碼
  4. Body : 打印請求和返回值的頭部和body信息。

總結

上面就是簡單的retrofit的使用,關於利用retrofit結合其他部分如Rx,okHttp等等,或者利用retrofit實現多種文件上傳,下載功能,保存cookie等等功能,可以期待后續的文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM