<del id="d4fwx"><form id="d4fwx"></form></del>
      <del id="d4fwx"><form id="d4fwx"></form></del><del id="d4fwx"><form id="d4fwx"></form></del>

            <code id="d4fwx"><abbr id="d4fwx"></abbr></code>
          • 如何在Retrofit中自定義請求參數(shù)注解

            這篇文章將為大家詳細講解有關(guān)如何在Retrofit中自定義請求參數(shù)注解,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

            石林網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)公司!從網(wǎng)頁設計、網(wǎng)站建設、微信開發(fā)、APP開發(fā)、響應式網(wǎng)站開發(fā)等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)公司成立與2013年到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設就選創(chuàng)新互聯(lián)公司。

            Retrofit 中使用方式

            先來看看在 Retrofit 中對于這兩種請求的聲明方式:

            GET 請求

            @GET("transporter/info")
            Flowable<Transporter> getTransporterInfo(@Query("uid") long id);

            我們使用 @Query 注解來聲明查詢參數(shù),每一個參數(shù)都需要用 @Query 注解標記

            POST 請求

            @POST("transporter/update")
            Flowable<ResponseBody> changBind(@Body Map<String,Object> params);

            在 Post 請求中,我們通過 @Body 注解來標記需要傳遞給服務器的對象

            Post 請求參數(shù)的聲明能否更直觀

            以上兩種常規(guī)的請求方式很普通,沒有什么特別要說明的。

            有次團隊討論一個問題,我們所有的請求都是聲明在不同的接口中的,如官方示例:

            public interface GitHubService {
             @GET("users/{user}/repos")
             Call<List<Repo>> listRepos(@Path("user") String user);
            }

            如果是 GET 請求還好,通過 @Query 注解我們可以直觀的看到請求的參數(shù),但如果是 POST 請求的話,我們只能夠在上層調(diào)用的地方才能看到具體的參數(shù),那么 POST 請求的參數(shù)聲明能否像 GET 請求一樣直觀呢?

            @Field 注解

            先看代碼,關(guān)于 @Field 注解的使用:

            @FormUrlEncoded
            @POST("user/edit")
            Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

            使用了 @Field 注解之后,我們將以表單的形式提交數(shù)據(jù)(first_name = XXX & last_name = yyy)。

            基于約定帶來的問題

            看上去 @Field 注解可以滿足我們的需求了,但遺憾的是之前我們和 API 約定了 POST 請求數(shù)據(jù)傳輸?shù)母袷綖?JSON 格式,顯然我們沒有辦法使用該注解了

            Retrofit 參數(shù)注解的處理流程

            這個時候我想是不是可以模仿 @Field 注解,自己實現(xiàn)一個注解最后使得參數(shù)以 JSON 的格式傳遞給 API 就好了,在此之前我們先來看看 Retrofit 中對于請求的參數(shù)是如何處理的:

            ServiceMethod 中 Builder 的構(gòu)造函數(shù)

            Builder(Retrofit retrofit, Method method) {
             this.retrofit = retrofit;
             this.method = method;
             this.methodAnnotations = method.getAnnotations();
             this.parameterTypes = method.getGenericParameterTypes();
             this.parameterAnnotationsArray = method.getParameterAnnotations();
            }

            我們關(guān)注三個屬性:

            • methodAnnotations 方法上的注解,Annotation[] 類型

            • parameterTypes 參數(shù)類型,Type[] 類型

            • parameterAnnotationsArray 參數(shù)注解,Annotation[][] 類型

            在構(gòu)造函數(shù)中,我們主要對這 5 個屬性賦值。

            Builder 構(gòu)造者的 build 方法

            接著我們看看在通過 build 方法創(chuàng)建一個 ServiceMethod 對象的過程中發(fā)生了什么:

            //省略了部分代碼...
            
            public ServiceMethod build() {
             //1. 解析方法上的注解
             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];
            
             Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
             //2. 通過循環(huán)為每一個參數(shù)創(chuàng)建一個參數(shù)處理器
             parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
             }
             return new ServiceMethod<>(this);
            }

            解析方法上的注解 parseMethodAnnotation

            if (annotation instanceof GET) {
             parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
            }else if (annotation instanceof POST) {
             parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
            }

            我省略了大部分的代碼,整段的代碼其實就是來判斷方法注解的類型,然后繼續(xù)解析方法路徑,我們僅關(guān)注 POST 這一分支:

            private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
             this.httpMethod = httpMethod;
             this.hasBody = hasBody;
             // Get the relative URL path and existing query string, if present.
             // ...
            }

            可以看到這條方法調(diào)用鏈其實就是確定 httpMethod 的值(請求方式:POST),hasBody(是否含有 Body 體)等信息

            創(chuàng)建參數(shù)處理器

            在循環(huán)體中為每一個參數(shù)都創(chuàng)建一個 ParameterHandler:

            private ParameterHandler<?> parseParameter(
             int p, Type parameterType, Annotation[] annotations) {
             ParameterHandler<?> result = null;
             for (Annotation annotation : annotations) {
             ParameterHandler<?> annotationAction = parseParameterAnnotation(
             p, parameterType, annotations, annotation);
             }
             // 省略部分代碼...
             return result;
            }

            可以看到方法內(nèi)部接著調(diào)用了 parseParameterAnnotation 方法來返回一個參數(shù)處理器:

            對于 @Field 注解的處理

            else if (annotation instanceof Field) {
             Field field = (Field) annotation;
             String name = field.value();
             boolean encoded = field.encoded();
            
             gotField = true;
             Converter<?, String> converter = retrofit.stringConverter(type, annotations);
             return new ParameterHandler.Field<>(name, converter, encoded);
            
            }
            • 獲取注解的值,也就是參數(shù)名

            • 根據(jù)參數(shù)類型選取合適的 Converter

            • 返回一個 Field 對象,也就是 @Field 注解的處理器

            ParameterHandler.Field

            //省略部分代碼
            static final class Field<T> extends ParameterHandler<T> {
             private final String name;
             private final Converter<T, String> valueConverter;
             private final boolean encoded;
            
             //構(gòu)造函數(shù)...
            
             @Override
             void apply(RequestBuilder builder, @Nullable T value) throws IOException {
             String fieldValue = valueConverter.convert(value);
             builder.addFormField(name, fieldValue, encoded);
             }
            }

            通過 apply 方法將 @Filed 標記的參數(shù)名,參數(shù)值添加到了 FromBody 中

            對于 @Body 注解的處理

            else if (annotation instanceof Body) {
             Converter<?, RequestBody> converter;
             try {
             converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
             } catch (RuntimeException e) {
             // Wide exception range because factories are user code.throw parameterError(e, p, "Unable to create @Body converter for %s", type);
             }
             gotBody = true;
             return new ParameterHandler.Body<>(converter);
            }
            • 選取合適的 Converter

            • gotBody 標記為 true

            • 返回一個 Body 對象,也就是 @Body 注解的處理器

            ParameterHandler.Body

             static final class Body<T> extends ParameterHandler<T> {
             private final Converter<T, RequestBody> converter;
            
             Body(Converter<T, RequestBody> converter) {
             this.converter = converter;
             }
            
             @Override
             void apply(RequestBuilder builder, @Nullable T value) {
             RequestBody body;
             try {
             body = converter.convert(value);
             } catch (IOException e) {
             throw new RuntimeException("Unable to convert " + value + " to RequestBody", e);
             }
             builder.setBody(body);
             }
            }

            通過 Converter 將 @Body 聲明的對象轉(zhuǎn)化為 RequestBody,然后設置賦值給 body 對象

            apply 方法什么時候被調(diào)用

            我們來看看 OkHttpCall 的同步請求 execute 方法:

            //省略部分代碼...
            @Override
            public Response<T> execute() throws IOException {
             okhttp3.Call call;
            
             synchronized (this) {
             call = rawCall;
             if (call == null) {
             try {
             call = rawCall = createRawCall();
             } catch (IOException | RuntimeException | Error e) { throwIfFatal(e); // Do not assign a fatal error to creationFailure.
             creationFailure = e;
              throw e;
             }
             }
             return parseResponse(call.execute());
            }

            在方法的內(nèi)部,我們通過 createRawCall 方法來創(chuàng)建一個 call 對象,createRawCall 方法內(nèi)部又調(diào)用了 serviceMethod.toRequest(args);方法來創(chuàng)建一個 Request 對象:

            /**
             * 根據(jù)方法參數(shù)創(chuàng)建一個 HTTP 請求
             */
            Request toRequest(@Nullable Object... args) throws IOException {
             RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers, contentType, hasBody, isFormEncoded, isMultipart);
             ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
            
             int argumentCount = args != null ? args.length : 0;
             if (argumentCount != handlers.length) {
             throw new IllegalArgumentException("Argument count (" + argumentCount
             + ") doesn't match expected count (" + handlers.length + ")");
             }
            
             for (int p = 0; p < argumentCount; p++) {
             handlers[p].apply(requestBuilder, args[p]);
             }
            
             return requestBuilder.build();
            }

            可以看到在 for 循環(huán)中執(zhí)行了每個參數(shù)對應的參數(shù)處理器的 apply 方法,給 RequestBuilder 中相應的屬性賦值,最后通過 build 方法來構(gòu)造一個 Request 對象,在 build 方法中還有至關(guān)重要的一步:就是確認我們最終的 Body 對象的來源,是來自于 @Body 注解聲明的對象還是來自于其他

            RequestBody body = this.body;
            if (body == null) {
             // Try to pull from one of the builders.
             if (formBuilder != null) {
             body = formBuilder.build();
             } else if (multipartBuilder != null) {
             body = multipartBuilder.build();
             } else if (hasBody) {
             // Body is absent, make an empty body.
             body = RequestBody.create(null, new byte[0]);
             }
            }

            自定義 POST 請求的參數(shù)注解 @BodyQuery

            根據(jù)上述流程,想要自定義一個參數(shù)注解的話,涉及到以下改動點:

            • 新增類 @BodyQuery 參數(shù)注解

            • 新增類 BodyQuery 用來處理 @BodyQuery 聲明的參數(shù)

            • ServiceMethod 中的 parseParameterAnnotation 方法新增對 @BodyQuery 的處理分支

            • RequestBuilder 類,新增 boolean 值 hasBodyQuery,表示是否使用了 @BodyQuery 注解,以及一個 Map 對象 hasBodyQuery,用來存儲 @BodyQuery 標記的參數(shù)

            @BodyQuery 注解

            public @interface BodyQuery {
             /**
             * The query parameter name.
             */
             String value();
            
             /**
             * Specifies whether the parameter {@linkplain #value() name} and value are already URL encoded.
             */
             boolean encoded() default false;
            }

            沒有什么特殊的,copy 的 @Query 注解的代碼

            BodyQuery 注解處理器

            static final class BodyQuery<T> extends ParameterHandler<T> {
             private final String name;
             private final Converter<T, String> valueConverter;
            
             BodyQuery(String name, Converter<T, String> valueConverter) {
             this.name = checkNotNull(name, "name == null");
             this.valueConverter = valueConverter;
             }
            
             @Override
             void apply(RequestBuilder builder, @Nullable T value) throws IOException {
             String fieldValue = valueConverter.convert(value);
             builder.addBodyQueryParams(name, fieldValue);
             }
            }

            在 apply 方法中我們做了兩件事

            • 模仿 Field 的處理,獲取到 @BodyQuery 標記的參數(shù)值

            • 將鍵值對添加到一個 Map 中

            // 在 RequestBuilder 中新增的方法
            void addBodyQueryParams(String name, String value) {
             bodyQueryMaps.put(name, value);
            }

            針對 @BodyQuery 新增的分支處理

            else if (annotation instanceof BodyQuery) {
             BodyQuery field = (BodyQuery) annotation;
             String name = field.value();
             hasBodyQuery = true;
            
             Converter<?, String> converter = retrofit.stringConverter(type, annotations);
             return new ParameterHandler.BodyQuery<>(name, converter);
            }

            我省略對于參數(shù)化類型的判斷,可以看到這里的處理和對于 @Field 的分支處理基本一致,只不過是返回的 ParameterHandler 對象類型不同而已

            RequestBuilder

            之前我們說過在 RequestBuilder#build() 方法中最重要的一點是確定 body 的值是來自于 @Body 還是表單還是其他對象,這里需要新增一種來源,也就是我們的 @BodyQuery 注解聲明的參數(shù)值:

            RequestBody body = this.body;
            if (body == null) {
             // Try to pull from one of the builders.
             if (formBuilder != null) {
             body = formBuilder.build();
             } else if (multipartBuilder != null) {
             body = multipartBuilder.build();
             } else if (hasBodyQuery) {
             body = RequestBody.create(MediaType.parse("application/json; charset=UTF-8"), JSON.toJSONBytes(this.bodyQueryMaps));
            
             } else if (hasBody) {
             // Body is absent, make an empty body.
             body = RequestBody.create(null, new byte[0]);
             }
            }

            在 hasBodyQuery 的分支,我們會將 bodyQueryMaps 轉(zhuǎn)換為 JSON 字符串然后構(gòu)造一個 RequestBody 對象賦值給 body。

            最后

            通過一個例子來看一下 @BodyQuery 注解的使用:

            @Test
            public void simpleBodyQuery(){
             class Example{
             @POST("/foo")
             Call<ResponseBody> method(@BodyQuery("A") String foo,@BodyQuery("B") String ping){
              return null;
             }
             }
             Request request = buildRequest(Example.class,"hello","world");
             assertBody(request.body(), "{\"A\":\"hello\",\"B\":\"world\"}");
            }

            關(guān)于如何在Retrofit中自定義請求參數(shù)注解就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

            名稱欄目:如何在Retrofit中自定義請求參數(shù)注解
            本文路徑:http://www.jbt999.com/article22/gjsdcc.html

            成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供ChatGPT、網(wǎng)站收錄、關(guān)鍵詞優(yōu)化、標簽優(yōu)化網(wǎng)站設計、Google

            廣告

            聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:[email protected]。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

            搜索引擎優(yōu)化

              <del id="d4fwx"><form id="d4fwx"></form></del>
              <del id="d4fwx"><form id="d4fwx"></form></del><del id="d4fwx"><form id="d4fwx"></form></del>

                    <code id="d4fwx"><abbr id="d4fwx"></abbr></code>
                  • 先锋资源久久 | 青青草无码在线 | 成人黄色免费看 | 日韩啪啪视频 | www午夜视频 |