retrofit 英文名字是改裝的意思,也就是說他是對網絡請求的一種改裝,他不負責進行網絡請求,他是對請求方式的一種封裝。真正進行網絡請求的是okhttp。
如下全部內容在Android Studio已經導入retrofit爲基礎。導入方式以下:html
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請求。json
咱們要獲取百度頁面的HTML。首先構建以下接口:api
public interface HtmlService { @GET("/") Call<String> getHtml(); }
注意,GET註解中的參數,和方法中的參數至少要加一個,不然會報錯。因爲,咱們只須要請求www.baidu.com
,因此get這裏不須要加參數,就是/
而後,咱們經過以下步驟,來進行網絡請求。
在咱們須要進行網絡請求的類中,經過如下的步驟,進行網絡請求:cookie
第一步網絡
Retrofit retrofit = new Retrofit.Builder(). addConverterFactory(ScalarsConverterFactory.create()). baseUrl("https://www.baidu.com"). build();
經過以上代碼,能夠簡單的構建一個retrofit對象,addConverterFactory
是對response進行解析,裏面添加的參數是表示對response用String解析,而後添加一個基礎的URL,後續的參數則是經過上面咱們定製的接口來添加,最後構建一個完整的URL。
第二步app
HtmlService htmlService = retrofit.create(HtmlService.class);
經過動態代理,生成一個接口的對象。異步
第三步ide
Call<String> call = htmlService.getHtml();
經過接口的方法獲得調用的對象。post
第四步與第五步
異步方法獲得response:ui
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();
經過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方法是沒有查詢參數的,下面對一個有查詢參數的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
。第一步
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請求,看看是否有簡單的方式,由於不想在一個方法裏,傳入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的集合傳入方法中,後續的步驟不變,就很少數。
利用retorfit進行post請求與進行get請求沒有太多的區別。主要的區別就在構建接口方法上面,有一些不一樣,下面經過一些實驗來看一下具體的區別。
下面的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方法同樣,就很少說了。
這個只不過構建接口方法的時候,有所區別,其餘步驟和多種參數進行GET請求同樣。
@FormUrlEncoded @POST("login") Call<String> doLogin(@FieldMap Map<String,String> fields);
將多個請求參數保存到Map中,傳入方法中,沒什麼好說的。
一樣構建一個接口方法:
@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 請求。
咱們在進行復雜的網絡請求的時候,一般要對請求添加頭部,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等等功能,能夠期待後續的文章。