retrofit 英文名字是改裝的意思,也就是說他是對網路請求的一種改裝,他不負責進行網路請求,他是對請求方式的一種封裝。真正進行網路請求的是okhttp。 以下所有內容在Android Studio已經導入retrofit為基礎。導入方式如下: 利用Retrofit進行簡單的GET請求 retro ...
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這裡不需要加參數,就是/
然後,我們通過如下步驟,來進行網路請求。
在我們需要進行網路請求的類中,通過以下的步驟,進行網路請求:
- 構建retrofit對象。
- 動態代理生成介面的對象。
- 通過介面的方法,得到要請求的API的調用。
- 通過同步/非同步的方式,得到response。
- 根據需要,處理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方法都需要的,我們就會通過一個攔截器的形式,統一在請求的時候加上。步驟如下:
- 自定義一個攔截器實現
Interceptor
。 - 創建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提供了多種添加方式,可以分為如下兩種:
- 靜態添加頭部
- 動態添加頭部
靜態添加頭部
靜態添加頭部,則每次請求的時候,頭部的信息都是固定的,不可以更改的。添加靜態頭部的方式也有多種,方法如下:
- 在介面中添加。
- 通過攔截器添加。
下麵,我們分別說一說每一種。
在介面中添加
還是以上文寫到的介面為例,添加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,下麵說一下具體的級別以及其內涵:
NONE
: 沒有任何日誌信息。Basic
: 列印請求類型,URL,請求體大小,返回值狀態以及返回值的大小。Headers
: 列印返回請求和返回值的頭部信息,請求類型,URL以及返回值狀態碼Body
: 列印請求和返回值的頭部和body信息。
總結
上面就是簡單的retrofit的使用,關於利用retrofit結合其他部分如Rx,okHttp等等,或者利用retrofit實現多種文件上傳,下載功能,保存cookie等等功能,可以期待後續的文章。