<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] # 概述 * Retrofit 對網絡請求接口進行了封裝,實際執行網絡請求的依然是 OkHttp * Retrofit 接口層實際是對 OkHttp 中 Request 的封裝,采用注解的形式來描述網絡請求參數 * Retrofit采用動態代理模式創建請求接口對象,請求執行調用接口方法時,Retrofit會根據注解創建相應的Call對象,接下來使用OkHttp發起請求 # Retrofit 使用 在學習 Retrofit 使用之前我們先回顧下 OKHttp 的工作方式,一個標準的 OkHttp 請求格式為 `mClient.newCall(request).enqueue(callback);`。可以看到,首先構造出 Request 對象(經過封裝的 http 請求信息)和 OkHttpClient 對象,然后調用 OkHttpRequest 的 newCall 方法,傳入封裝的 request 對象后,就得到了一個 RealCall 對象,然后就可以操作 RealCall 對象執行請求、取消等一系列操作。 而 Retrofit 對于 http 請求信息并不是直接構造成 Request 對象,而是聲明為接口的形式,相關請求信息(地址、方法、請求頭等)使用注解和參數的形式傳入。同樣會有一個 Retrofit 客戶端對象,調用其 create 方法會幫我們創建出請求接口的實例對象,接著調用請求接口實例對象的方法發起請求。流程如下: * 創建請求接口 * 構建 Retrofit 實例 * 創建網絡請求接口實例 * 發起網絡請求 可以看到,Retrofit 對于 OkHttp 中的 Request 進行了抽象化為接口,在使用時首先構造出請求對象。一個標準的 Retrofit 請求格式為 `mRetrofit.create(RequestService.class).doGet("params").enqueue(callback)`,其中 doGet 方法返回的是一個 Call 對象。 # Retrofit 注解使用 來源:[Retrofit2.0中注解使用套路](https://blog.csdn.net/stven_king/article/details/52372172)、[Retrofit網絡請求參數注解,@Path、@Query、@QueryMap](https://www.jianshu.com/p/7687365aa946) ## 靜態 url 請求 ### GET 請求 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); public interface GitHubService { //無參數 // https://api.github.com/users/xcy396/repos @GET("users/xcy396/repos") Call<List<Repo>> listRepos(); //少數參數 //https://api.github.com/users/xcy396/repos?time={time} @GET("users/xcy396/repos") Call<List<Repo>> listRepos(@Query("time") long time); //參數較多 //https://api.github.com/users/xcy396/repos?time={time}&author={author}... @GET("users/xcy396/repos") Call<List<Repo>> listRepos(@QueryMap Map<String, String> params); } ``` ### POST 請求 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); public interface GitHubService { //無參數 @POST("users/xcy396/repos") Call<List<Repo>> listRepos(); //少數參數(參數拼接在url后面) //https://api.github.com/users/xcy396/repos?time={time} @POST("users/xcy396/repos") Call<List<Repo>> listRepos(@Query("time") long time); //少數參數(使用表單形式提交,參數在請求體) @FormUrlEncoded @POST("users/xcy396/repos") Call<List<Repo>> listRepos(@Field("time") long time); //參數較多(使用表單形式提交,參數在請求體) @FormUrlEncoded @POST("users/xcy396/repos") Call<List<Repo>> listRepos(@FieldMap Map<String, String> params); //參數較多(使用Plain形式提交,參數在請求體) @POST("users/xcy396/repos") Call<List<Repo>> listRepos(@Body Map<String, String> params); } ``` ### DELETE請求 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); public interface GitHubService { //無參數 // https://api.github.com/users/xcy396/repos @DELETE("users/xcy396/repos") Call<List<Repo>> listRepos(); //少數參數(參數拼接在url后面) //https://api.github.com/users/xcy396/repos?time={time} @DELETE("users/xcy396/repos") Call<List<Repo>> listRepos(@Query("time") long time); //參數較多(使用Plain形式提交,參數在請求體) //https://api.github.com/users/xcy396/repos @HTTP(method = "DELETE", path = "users/xcy396/repos", hasBody = true) Call<List<Repo>> listRepos(@Body Map<String, String> params); } ``` 注意: * 當@GET或@POST注解的url為全路徑時(可能和baseUrl不是一個域),會直接使用注解的url的域 * 如果請求為 Post 實現,那么最好傳遞參數時使用@Field、@FieldMap和@FormUrlEncoded。因為@Query和@QueryMap都是將參數拼接在url后面的,而@Field或@FieldMap傳遞的參數時放在請求體的 * 使用@Path時,path對應的路徑不能包含”/”,否則會將其轉化為%2F。在遇到想動態的拼接多節url時,還是使用@Url ## 半靜態 url 請求 https://api.github.com/users/{user}/repos ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build() public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); } ``` ## 動態 url 請求 ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build() public interface GitHubService { @GET Call<List<Repo>> listRepos(@Url String user); } ``` # 注解說明 ## 網絡請求方法 網絡請求方法注解對應于 Http 請求方式,包括: 注解名稱|說明 ---|--- @GET|從服務器取出資源(一項或多項) @POST|在服務器新建一個資源 @PUT|在服務器更新資源(客戶端提供改變后的完整資源) @DELETE|從服務器刪除資源 @HEAD|獲取資源的元數據 @OPTIONS|獲取信息,關于資源的哪些屬性是客戶端可以改變的 ## 標記類 標記類注解,作用于網絡請求接口的方法,包括: 注解名稱|說明 ---|--- @FormUrlEncoded|表示請求體是一個 Form 表單 @Multipart|表示請求體是一個 Multipart 表單 @Streaming|表示返回的數據以流的形式返回 ### 提交表單 代碼示例: ```java public interface Add2GankApi { @POST("add2gank") @FormUrlEncoded Call<ResponseBody> add(@Field("url") String url, @Field("desc") String desc, @Field("who") String who, @Field("type") String type, @Field("debug") boolean debug); } ``` ### 提交 Multipart 代碼示例: ```java public interface Add2GankApi { @POST("add2gank") @Multipart Call<ResponseBody> addFile(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file); } ``` 其中 @Part 后面括號內的字符用于生成每個 Part 請求頭 Content-Disposition 字段的信息,接收的參數則用于生成請求體。生成的 Content-Disposition 字段示例如下: ```plain Content-Disposition: form-data; name=”name” 或 Content-Disposition: form-data; name=”test”; filename=”test.txt” ``` 調用: ```java RequestBody name = RequestBody.create(mediaType, "Tom"); RequestBody age = RequestBody.create(mediaType, "12"); MultipartBody.Part file= MultipartBody.Part.createFormData("test", "test.txt", RequestBody.create(mediaType, new File("test.txt"))); RetrofitUtil.getInstance() .createAdd2GankApiRequest() .addFile(name, age, file); ``` ## 網絡請求參數注解 網絡請求參數注解包括: 注解名稱|說明 ---|--- @Headers|添加固定的請求頭 @Header|添加不固定值的 Header 示例: ```java @GET("user") Call<ResponseBody> getUser(@Header("Authorization") String authorization); @GET("user") @Headers("Authorization: authorization") Call<ResponseBody> getUser(); ``` 注解名稱|說明 ---|--- @Body|發送自定義數據類型(非表單數據)給服務器 注解名稱|說明 ---|--- @Field|表單字段 @FieldMap|表單字段 示例: ```java @POST("add2gank") @FormUrlEncoded Call<ResponseBody> add(@Field("username") String name, @Field("age") int age); @POST("add2gank") @FormUrlEncoded Call<ResponseBody> add(@FieldMap Map<String, Object> map); ``` 使用: ```java RetrofitUtil.getInstance() .createAdd2GankApiRequest() .add("Tom", 18); Map<String, Object> map = new HashMap<>(); map.put("username", "Tom"); map.put("age", 18); RetrofitUtil.getInstance() .createAdd2GankApiRequest() .add(map); ``` 注解名稱|說明 ---|--- @Part|Multipart 表單字段 @PartMap|Multipart 表單字段 示例: ```java public interface Add2GankApi { @POST("add2gank") @Multipart Call<ResponseBody> addFile(@Part("name") RequestBody name, @Part("age") RequestBody age, @Part MultipartBody.Part file); @POST("add2gank") @Multipart Call<ResponseBody> addFile(@PartMap Map<String, RequestBody> args, @Part MultipartBody.Part file); } ``` 使用 ```java // Part 使用 RequestBody name = RequestBody.create(mediaType, "Tom"); RequestBody age = RequestBody.create(mediaType, "12"); MultipartBody.Part file= MultipartBody.Part.createFormData("test", "test.txt", RequestBody.create(mediaType, new File("test.txt"))); RetrofitUtil.getInstance() .createAdd2GankApiRequest() .addFile(name, age, file); // PartMap 使用 Map<String, RequestBody> map = new HashMap<>(); map.put("name", name); map.put("age", age); RetrofitUtil.getInstance() .createAdd2GankApiRequest() .addFile(map, file); ``` 注解名稱|說明 ---|--- @Query|作用于 GET 方法的查詢參數,作用于 Url @QueryMap|作用于 GET 方法的查詢參數,作用于 Url 示例: ```java @GET("user") Call<ResponseBody> getUser(@Query("age") int age, @Query("gender") int gender); ``` 在 baseUrl = "http://gank.io/api/",調用 getUser 方法傳參為(18, 0)時,請求 url 為 "http://gank.io/api/?age=18&gender=0" 注解名稱|說明 ---|--- @Path|URL 地址的缺省值 @Url|直接設置 Url 變量 示例: ```java @GET("users/{user}/photos") Call<ResponseBody> getUserPhotos(@Path("user") String user); ``` 當傳參為 "tom" 時,請求 url 為 "http://gank.io/api/users/tom/photos"。 示例: ```java @GET Call<ResponseBody> getUSer(@Url String url, @Query("age") int age); ``` 當有 @Url 注解時,@GET 傳入的 Url 可以忽略。 # 源碼分析 ## 具體分析 ### Retrofit 的構造 我們首先來從 Retrofit 對象的構建說起。和 OkHttpClient 一樣,Retrofit 對象我們也建議項目中只維護一個。一個標準的 Retrofit 構造代碼如下: ```java Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .build(); ``` 可以看到 Retrofit 也使用了建造者模式, ### Request對象的封裝與Call的生成 一個標準的 Retrofit 請求格式為 `mRetrofit.create(RequestService.class).doGet("params").enqueue(callback)`。 在OkHttp中,我們手動創建了Request對象,并創建Call對象。而在Retrofit中,則是首先聲明請求接口(通過注解聲明請求地址、方法、請求體等),Retrofit的create方法會為我們創建一個請求接口對象。先來看看create方法: ```java public <T> T create(final Class<T> service) { // 驗證接口的合法性 Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } // 創建 ServiceMethod ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method); // 創建 OkHttpCall OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args); // 返回 return serviceMethod.adapt(okHttpCall); } }); } ``` Retrofit使用了動態代理模式(可參考[代理模式]([http://wiki.xuchongyang.com/1091154#2\_\_57](http://wiki.xuchongyang.com/1091154#2__57))),create方法返回了請求接口的代理對象。當我們調用代理對象的方法時,會被攔截,執行最后三行關鍵代碼,下面我們先看看ServiceMethod類,再依次看最后三行代碼。 1、ServiceMethod類就是我們定義的請求接口到Call的轉換適配器。 ```java public class ServiceMethod { Builder(Retrofit retrofit, Method method) { this.retrofit = retrofit; this.method = method; this.methodAnnotations = method.getAnnotations(); this.parameterTypes = method.getGenericParameterTypes(); this.parameterAnnotationsArray = method.getParameterAnnotations(); } public ServiceMethod build() { //... // 解析方法注解 for (Annotation annotation : methodAnnotations) { parseMethodAnnotation(annotation); } //... int parameterCount = parameterAnnotationsArray.length; parameterHandlers = new ParameterHandler<?>[parameterCount]; for (int p = 0; p < parameterCount; p++) { Type parameterType = parameterTypes[p]; if (Utils.hasUnresolvableType(parameterType)) { throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s", parameterType); } Annotation[] parameterAnnotations = parameterAnnotationsArray[p]; if (parameterAnnotations == null) { throw parameterError(p, "No Retrofit annotation found."); } // 解析參數 parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations); } // ... return new ServiceMethod<>(this); } private void parseMethodAnnotation(Annotation annotation) { if (annotation instanceof DELETE) { parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false); } else if (annotation instanceof GET) { parseHttpMethodAndPath("GET", ((GET) annotation).value(), false); } else if (annotation instanceof HEAD) { parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false); if (!Void.class.equals(responseType)) { throw methodError("HEAD method must use Void as response type."); } } else if (annotation instanceof PATCH) { parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true); } else if (annotation instanceof POST) { parseHttpMethodAndPath("POST", ((POST) annotation).value(), true); } else if (annotation instanceof PUT) { parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true); } else if (annotation instanceof OPTIONS) { parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false); } else if (annotation instanceof HTTP) { HTTP http = (HTTP) annotation; parseHttpMethodAndPath(http.method(), http.path(), http.hasBody()); } else if (annotation instanceof retrofit2.http.Headers) { String[] headersToParse = ((retrofit2.http.Headers) annotation).value(); if (headersToParse.length == 0) { throw methodError("@Headers annotation is empty."); } headers = parseHeaders(headersToParse); } else if (annotation instanceof Multipart) { if (isFormEncoded) { throw methodError("Only one encoding annotation is allowed."); } isMultipart = true; } else if (annotation instanceof FormUrlEncoded) { if (isMultipart) { throw methodError("Only one encoding annotation is allowed."); } isFormEncoded = true; } } } ``` 從代碼中可以看到,我們使用注解定義的所有信息,在構造ServiceMethod時進行了解析并存儲。 2、接下來看看剛剛create方法的倒數第三行代碼`ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);`。 其中loadServiceMethod方法源碼如下: ```java ServiceMethod<?, ?> loadServiceMethod(Method method) { ServiceMethod<?, ?> result = serviceMethodCache.get(method); if (result != null) return result; synchronized (serviceMethodCache) { result = serviceMethodCache.get(method); if (result == null) { result = new ServiceMethod.Builder<>(this, method).build(); serviceMethodCache.put(method, result); } } return result; } ``` 可以看到Retrofit對ServiceMethod實例進行了緩存,來提高請求時的解析效率。 3、看下倒數第二行代碼`OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);`。這行代碼根據serviceMethod對象構造了一個OkHttpCall對象。 OkHttpCall類源碼如下: ```java final class OkHttpCall<T> implements Call<T> { private final ServiceMethod<T, ?> serviceMethod; private final @Nullable Object[] args; OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) { this.serviceMethod = serviceMethod; this.args = args; } private okhttp3.Call createRawCall() throws IOException { okhttp3.Call call = serviceMethod.toCall(args); if (call == null) { throw new NullPointerException("Call.Factory returned null."); } return call; } } ``` 可以看到,OkHttpCall實現了Call接口,可以直接作為Call執行一系列操作,并且有一個createRawCall方法,會在調用request、enqueue、execute方法時進行調用。 4、得到OkHttpCall對象后,來看看最后一行代碼`return serviceMethod.adapt(okHttpCall);` 來看下ServiceMethod的adapt方法: ```java T adapt(Call<R> call) { return callAdapter.adapt(call); } ``` 下面我們按兩條線來走,一是CallAdapter是怎么來的呢,二是CallAdapter的adapt方法做了什么。 a、首先來看看CallAdapter是怎么來的呢? ```java private CallAdapter<T, R> createCallAdapter() { Type returnType = method.getGenericReturnType(); Annotation[] annotations = method.getAnnotations(); //... return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations); } // Retrofit類的callAdapter方法如下: public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) { return nextCallAdapter(null, returnType, annotations); } public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) { int start = callAdapterFactories.indexOf(skipPast) + 1; for (int i = start, count = callAdapterFactories.size(); i < count; i++) { CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this); if (adapter != null) { return adapter; } } } ``` 可以看到,從CallAdapterFactories中遍歷查詢,看是否有返回類型相匹配的CallAdpater。下面再看看CallAdapterFactories是怎么來的。 ```java public Retrofit build() { //... // 添加默認的CallAdapterFactory List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); //... } ``` 在Retrofit的構造方法中,會添加平臺的默認CallAdapterFactory。CallAdapterFactory有兩個子類:DefaultCallAdapterFactory和ExecutorCallAdapterFactory。默認的CallAdapterFactory是ExecutorCallAdapterFactory: ```java final class ExecutorCallAdapterFactory extends Factory { public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { //... return new CallAdapter<Object, Call<?>>() { public Type responseType() { return responseType; } public Call<Object> adapt(Call<Object> call) { return new ExecutorCallAdapterFactory.ExecutorCallbackCall(ExecutorCallAdapterFactory.this.callbackExecutor, call); } }; } static final class ExecutorCallbackCall<T> implements Call<T> { final Executor callbackExecutor; final Call<T> delegate; ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; } public void enqueue(final Callback<T> callback) { //... } public Response<T> execute() throws IOException { //... } } } ``` b、下面看看第二個問題,CallAdapter的adapt方法到底做了什么呢?由上面的ExecutorCallAdapterFactory可看到,adapt方法返回了一個Call接口的實現類。 c、最后,我們再多來看一下CallAdapter的聲明 ```java public interface CallAdapter<R, T> { Type responseType(); T adapt(Call<R> call); abstract class Factory { public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit); protected static Type getParameterUpperBound(int index, ParameterizedType type) { return Utils.getParameterUpperBound(index, type); } protected static Class<?> getRawType(Type type) { return Utils.getRawType(type); } } } ``` # 參考 [這是一份很詳細的 Retrofit 2.0 使用教程(含實例講解)](http://blog.csdn.net/carson_ho/article/details/73732076) [RESTful API 設計指南](http://www.ruanyifeng.com/blog/2014/05/restful_api.html) [Android:手把手帶你 深入讀懂 Retrofit 2.0 源碼]([https://www.jianshu.com/p/0c055ad46b6c](https://www.jianshu.com/p/0c055ad46b6c))
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看