前言
現在的網絡請求開源項目層出不窮,優秀者也不勝枚舉。常用的Volley,OkHttp等等。層出不窮的優秀開源工具,使得開發者的開發效率大幅提高,並且更加專注於自身的業務。今天介紹一下Retrofit的基本使用方法。
聲明在前
由於retrofit1.x和retrofit2.x升級變化很大,而有些教程在進行知識講解時並未之處針對的retrofit版本,這一點很讓初學者頭痛。現在您將看到的是關於retrofit2.x的講解。另外,關於retrofit1.x和retrofit2.x推薦一個不錯的網站:https://futurestud.io,該網站關於retrofit1.x有詳細的系列講解,並且文章也出了retrofit2.x系列,且沒有刪除retrofit1.x的相關講解。非常的不錯,可惜的是英文的。retrofit2使用了其默認的okhttpClient,當然你也可以自定義client。不過此時你需要關聯OkHttp。
閒聊retrofit
函數式編程使得代碼更易於閱讀,更加直觀、簡潔。retrofit也借鑑了這方面的有點。這是一個比較好的趨勢。與此同時,註解在retrofit的大量應用,使得網絡請求變得更加輕量級。大大減少了工作量,例如GET, POST, PUT, DELETE, 和 HEAD的請求參數都可以通過註解的方式來實現。retrofit涵蓋了主要的網絡請求方式。
另外,如果想要更深入的學習retrofit,對註解,反射的掌握是前提條件,如果想要更深入,還要學習一下java的動態代理技術。
retrofit的使用
首先需要將http api定義在接口中
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
其中Get註解代表這是一個get請求,大括號{}包含了請求api的可變部分。在listRepos中通過註解指定get請求路徑中的可變參數user。這是一個比較簡單的api接口。稍後會列出一些混合的複雜的接口實現。List是請求回的數據的封裝。
直接使用
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
通過被創建的GitHubService可以調用call方法實現一個同步或者異步的網絡請求:
Call<List<Repo>> repos = service.listRepos("octocat");
一些複雜的請求接口
添加請求參數
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
添加多個請求參數
@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);
指定請求頭
方式一:
@Headers({
"Accept: application/vnd.github.v3.full+json",
"User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);
方式二:
@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)
以上請求都是同步的;
如果使用異步網絡請求,需要在以上網路接口中增加一個回調例如:
@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);
序列化方式
默認的是RequestBody和ResponseBody
另外提供了以下converter
Gson - com.squareup.retrofit2:converter-gson
Jackson - com.squareup.retrofit2:converter-jackson
Moshi - com.squareup.retrofit2:converter-moshi
Protobuf - com.squareup.retrofit2:converter-protobuf
Wire - com.squareup.retrofit2:converter-wire
Simple Framework - com.squareup.retrofit2:converter-simpleframework
Scalars - com.squareup.retrofit2:converter-scalars
附錄:官方教程
到底怎麼使用Retrofit呢?
Retrofit通過註解和動態代理來進行網絡請求。所以充分了解Retrofit的註解非常的重要:
上圖是Retrofit中關於網絡請求相關的所有註解。
例如想要添加請求頭
如果你想添加請求頭:
retrofit提供了HEAD和Header和Headers三個註解,供你使用。
關於HEAD:
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HEAD {
String value() default "";
}
請注意HEAD是用在方法的註解的。但這是請求頭參數較少的請求。此外還可以使用Header
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Header {
String value();
}
Header是用在參數中的。
再說說Headers上面已經提供了一個service實例。
看看源碼:
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Headers {
String[] value();
}
所以無論你的請求想要添加什麼參數,先查看一下retrofit關於註解的源碼:
關於@Target的類型
- CONSTRUCTOR:用於描述構造器
- FIELD:用於描述域
- LOCAL_VARIABLE:用於描述局部變量
- METHOD:用於描述方法
- PACKAGE:用於描述包
- PARAMETER:用於描述參數
- TYPE:用於描述類、接口(包括註解類型) 或enum聲明
瞭解到這兒,至少你應該具有獨立使用retrofit的能力了。
作爲擴展知識,還是希望能研究一下retrofit的源碼。另外推薦一下Rxjava。二者結合使用,效率更高啊!
優秀文章:
關於retrofit的一個坑爹的提示
這個提示可能稱不上是bug,先來描述一下這個提示:
can't resolve host
在github的issues裏面找了好久也沒找到合適的答案,由於我的手機設置了代理,當我取消代理時,這個問題就解決了。真是好坑啊!!!!!!!!!!!!!!!!!
使用案例
案例1:爲請求添加頭
可以通過添加註解@Headers在endpoint上,也可以通過okhttp的插入器,來進行添加頭。
通過@Headers來添加頭
這個例子很好找,在retrofit的官方介紹中:
@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();
通過okhttp的插入器實現
okHttpClient.interceptors().add(new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "auth-value"); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
不過此時要注意Request.Builder的header(key,value)和addHeader(key,value)的區別。
.header(key, val): will override preexisting headers identified by key//將會覆蓋已存在的header
.addHeader(key, val): will add the header and don’t override preexisting ones//將會添加到header而不是覆蓋
retrofit2的轉換器轉換爲字符串
這是一個字符串轉換器,有來做測試比較方便
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public class ToStringConverterFactory extends Converter.Factory {
private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<ResponseBody, String>() {
@Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
};
}
return null;
}
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<String, RequestBody>() {
@Override
public RequestBody convert(String value) throws IOException {
return RequestBody.create(MEDIA_TYPE, value);
}
};
}
return null;
}
}
一些未知的小事項
最好不要通過插入器修改鏈接,會造成一些錯誤的結果,例如:重複修改鏈接,這個問題有待查明!