官方的Retrofit主頁是這樣描述它的
A type-safe HTTP client for Android and java
用于Android和Java的一個類型安全(type-safe)的REST客戶端
你可以使用注解去描述HTTP請求,同時Retrofit默認集成URL參數替換和查詢參數.除此之外它還支持 Multipart請求和文件上傳。
注意這個任務是網絡任務,不要忘記給程序加入網絡權限
<uses-permission android:name="android.permission.INTERNET" />build.gradle
dependencies { // Retrofit & OkHttpcompile 'com.squareup.retrofit:retrofit:2.0.0-beta2'}Retrofit 需要最低JDK 1.7 和 Android 2.3.
retfofit 使用注解的方式定義API
方法類型有:
GET, POST, PUT, DELETE, HEAD
1 聲明API
public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user);}2 初始化Retrofit
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.github.com/").build();GitHubService service = retrofit.create(GitHubService.class);Call<List<Repo>> repos = service.listRepos("octocat");3 發起請求:
//同步請求
try { Response<List<Repo>> bodyResponse = call.execute(); String body = bodyResponse.body().string();//獲取返回體的字符串 Log.i("wxl", "body=" + body); } catch (IOException e) { e.PRintStackTrace(); }//異步請求
call.enqueue(new Callback<List<Repo>>() { @Override public void onResponse(Response<List<Repo>> response) { try { Log.i("wxl", "response=" + response.body().string()); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Throwable t) { Log.i("wxl", "onFailure=" + t.getMessage()); } });4 取消請求
call.cancel();所有的請求方法都是按照上述類型執行。 在方法聲明時注解@GET,@POST,@PUT 分別對應方法的類型。
接口參數
Path
ApiStores
/** * Call<T> get();必須是這種形式,這是2.0之后的新形式 * 如果不需要轉換成Json數據,可以用了ResponseBody; * 你也可以使用Call<GsonBean> get();這樣的話,需要添加Gson轉換器 */public interface ApiStores { @GET("adat/sk/{cityId}.html") Call<ResponseBody> getWeather(@Path("cityId") String cityId);}Query
如果鏈接是http://ip.taobao.com/service/getIpInfo.php?ip=202.202.33.33
ApiStores
public interface ApiStores { @GET("http://ip.taobao.com/service/getIpInfo.php") Call<ResponseBody> getWeather(@Query("ip") String ip);復雜的查詢參數也可以使用Map。
@GET("group/{id}/users")Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);Headers
你可以通過@Headers參數來設置靜態headers
@Headers("Cache-Control: max-age=640000")@GET("widget/list")Call<List<Widget>> widgetList();@Headers({ "Accept: application/vnd.github.v3.full+json", "User-Agent: Retrofit-Sample-App"})@GET("users/{username}")Call<User> getUser(@Path("username") String username);Headers不復寫其他的Headers值。所有同名的的Headers參數都將包含在該請求中。 Request的Header可以通過使用@Header注解動態更新。必須給@Header提供一個相應的參數。如果該值為空,該Header就被忽略了。不然就會調用value的toString()方法獲得value的值。
每一個request都有的header可以通過設置OkHttp interceptor來統一添加。
Body
這是針對POST方式,如果參數是json格式,如:
{ "apiInfo": { "apiName": "WuXiaolong", "apiKey": "666" } }ApiStores
public interface ApiStores { @POST("client/shipper/getCarType") Call<ResponseBody> getCarType(@Body ApiInfo apiInfo); }這個Object將被Retrofit對象所指定的Converter所轉換,如果沒有指定轉換器,只能使用RequestBody來定義參數。
建立Bean
public class ApiInfo { private ApiInfoBean apiInfo; public ApiInfoBean getApiInfo() { return apiInfo; } public void setApiInfo(ApiInfoBean apiInfo) { this.apiInfo = apiInfo; } public class ApiInfoBean { private String apiName; private String apiKey; //省略get和set方法 } }代碼調用
private void getCarType() { mRetrofit = new Retrofit.Builder() .baseUrl("http://WuXiaolong.me/") .addConverterFactory(GsonConverterFactory.create()) .build(); ApiStores apiStores = mRetrofit.create(ApiStores.class); ApiInfo apiInfo = new ApiInfo(); ApiInfo.ApiInfoBean apiInfoBean = apiInfo.new ApiInfoBean(); apiInfoBean.setApiKey("666"); apiInfoBean.setApiName("WuXiaolong"); apiInfo.setApiInfo(apiInfoBean); Call<ResponseBody> call = apiStores.getCarType(apiInfo); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Response<ResponseBody> response) { String body = null;//獲取返回體的字符串 try { body = response.body().string(); } catch (IOException e) { e.printStackTrace(); } Log.i("wxl", "get=" + body); } @Override public void onFailure( Throwable t) { } }); }Url encode
POST or PUT Url encode 過的表單資料,用@FormUrlEncoded,使用@Field個別指定
@POST("/articles/{article_id}/comments")@FormUrlEncodedComment create(@Path("article_id") int articleId,@Field("rating") int rating,@Field("content") String content);POST or PUT Url encode 過的表單資料,用@FormUrlEncoded,參數也可用@Body傳
@POST("/articles/{article_id}/comments")@FormUrlEncodedComment create( @Path("article_id") int articleId, @Body Comment comment);POST or PUT 用@Multipart來上傳檔案
@PUT("/me")@MultipartMe update( @Part("display_name") String displayName, @Part("avatar") TypedFile avatar);更詳細的信息,請點擊:http://www.chenkaihua.com/2016/04/02/retrofit2-upload-multipart-files/
更多更有效的 Converters
默認情況下,Retrofit 只能反序列化HTTP body到OKHTTP的ResponseBody類型。并且OKHTTP只接受Retrifit @Body類型的RequestBody 可以添加Converter轉換器來支持其他的類型,6個相似的兼容流行的序列化庫模塊可以供使用。
例子:
interface which uses Gson for its deserialization.Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") .addConverterFactory(GsonConverterFactory.create()) .build();GitHubService service = retrofit.create(GitHubService.class);Retrofit 1 里有一個 converter 的問題。多數人可能沒遇到過,是庫內部的一個問題。在 Retrofit 2 里,已經解決了這個問題,同時開始支持多種 Converter 并存。
在之前,如果你遇到這種情況:一個 API 請求返回的結果需要通過 JSON 反序列化,另一個 API 請求需要通過 proto 反序列化,唯一的解決方>案就是將兩個接口分離開聲明。
interface SomeProtoService { @GET("/some/proto/endpoint") Call<SomeProtoResponse> someProtoEndpoint();}interface SomeJsonService { @GET("/some/json/endpoint") Call<SomeJsonResponse> someJsonEndpoint();之所以搞得這么麻煩是因為一個 REST adapter 只能綁定一個 Converter 對象。我們費工夫去解決這個是因為:接口的聲明是要語意化的。API 接口應該通過功能實現分組,比如: account 的接口,user 的接口,或者 Twitter 相關的接口。返回格式的差異不應該成為你分組時候的阻礙。
現在,你可以把他們都放在一起了:
interface SomeService { @GET("/some/proto/endpoint") Call<SomeProtoResponse> someProtoEndpoint(); @GET("/some/json/endpoint") Call<SomeJsonResponse> someJsonEndpoint();}SomeProtoResponse —> Proto? Yes!
原理很簡單,其實就是對著每一個 converter 詢問他們是否能夠處理某種類型。我們問 proto 的 converter: “Hi, 你能處理 SomeProtoResponse 嗎?”,然后它盡可能的去判斷它是否可以處理這種類型。我們都知道:Protobuff 都是從一個名叫 message 或者 message lite 的類繼承而來。所以,判斷方法通常就是檢查這個類是否繼承自 message。 在面對 JSON 類型的時候,首先問 proto converter,proto converter 會發現這個不是繼承子 Message 的,然后回復 no。緊接著移到下一個 JSON converter 上。JSON Converter 會回復說我可以!
SomeJsonResponse —> Proto? No! —> JSON? Yes!
因為 JSON 并沒有什么繼承上的約束。所以我們無法通過什么確切的條件來判斷一個對象是否是 JSON 對象。以至于 JSON 的 converters 會對任何數據都回復說:我可以處理!這個一定要記住, JSON converter 一定要放在最后,不然會和你的預期不符。
另一個要注意的是,現在已經不提供默認的 converter 了。如果不顯性的聲明一個可用的 Converter 的話,Retrofit 是會報錯的:提醒你沒有可用的 Converter。因為核心代碼已經不依賴序列化相關的第三方庫了,我們依然提供對 Converter 的支持,不過你需要自己引入這些依賴,同時顯性的聲明 Retrofit 需要用的 Converter 有哪些。 添加 converter 的順序很重要。按照這個順序,我們將依次詢問每一個 converter 能否處理一個類型。我上面寫的其實是錯的。如果我們試圖反序列化一個 proto 格式,它其實會被當做 JSON 來對待。這顯然不是我們想要的。我們需要調整下順序,因為我們先要檢查 proto buffer 格式,然后才是 JSON。
Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.github.com").addConverterFactory(ProtoConverterFactory.create()).addConverterFactory(GsonConverterFactory.create()).build();由于現在Retrofit開始依賴 OkHttp, 并沒有 Http Client 層的抽象。現在是可以傳遞一個配置好的 OkHttp 實例的。比如:配置 interceptors, 或者一個 SSL socket 工廠類, 或者 timeouts 的具體數值。 (OkHttp 有默認的超時機制,如果你不需要自定義,實際上不>必進行任何設置,但是如果你想要去設置它們,下面是一個例子告訴你來怎么操作。)
OkHttpClient client = new OkHttpClient.Builder() .retryOnConnectionFailure(true) //失敗后是否重試 .connectTimeout(15, TimeUnit.SECONDS) //設置連接超時時間 .addInterceptor(getHttpInterceptor()) //設置應用攔截器 .build();Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com") //設置BaseURL .client(client) //設置client .build();在使用Android retrofit+rxjava時,想獲知網絡請求的一些參數,方便調試,比如:請求地址、請求響應時間、請求響應消息體等內容,雖然部分可以通過每個接口進行獲知,但是這樣極其不方便,可以使用攔截器來做統一的操作。
添加攔截器: 可以針對OkHttpClient.Builder 添加攔截器
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); //設置日志級別OkHttpClient client = new OkHttpClient.Builder() .retryOnConnectionFailure(true) //失敗后是否重試 .connectTimeout(15, TimeUnit.SECONDS) //設置連接超時時間 .addInterceptor(interceptor) //設置應用攔截器 .build();開啟OKHttp緩存:
先獲取系統外部存儲的路徑,”xxx”可以自己命名,緩存文件就存在 Android/data/<包名>/cache/resposes。
File httpCacheDirectory = new File(UIUtils.getContext().getExternalCacheDir(), "xxx"); client.setCache(new Cache(httpCacheDirectory,10 * 1024 * 1024));如果你在你的代碼里使用代碼混淆機制,請在你的配置里添加下面幾行.
# Platform calls Class.forName on types which do not exist on Android to determine platform.-dontnote retrofit2.Platform# Platform used when running on RoboVM on iOS. Will not be used at runtime.-dontnote retrofit2.Platform$IOS$MainThreadExecutor# Platform used when running on Java 8 VMs. Will not be used at runtime.-dontwarn retrofit2.Platform$Java8# Retain generic type information for use by reflection by converters and adapters.-keepattributes Signature# Retain declared checked exceptions for use by a Proxy instance.-keepattributes ExceptionsRetrofit 在請求失敗時依然會回調 onResponse()方法。及時response code不是200依然會回調一下方法,需要注意。
Retrofit github主頁:https://square.github.io/retrofit/
Interceptor wiki: https://github.com/square/okhttp/wiki/Interceptors
用 Retrofit 2 簡化 HTTP 請求 https://realm.io/cn/news/droidcon-jake-wharton-simple-http-retrofit-2/
Http Caching: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn
四種常見的 POST 提交數據方式 https://imququ.com/post/four-ways-to-post-data-in-http.html
Blog http://wuxiaolong.me/2016/01/15/retrofit/
URL wikipedia: https://zh.wikipedia.org/wiki/%E7%BB%9F%E4%B8%80%E8%B5%84%E6%BA%90%E5%AE%9A%E4%BD%8D%E7%AC%A6
新聞熱點
疑難解答