Retrofit+Rxjava-ConverterFactory

作者: MonkeyLei | 来源:发表于2019-08-01 17:21 被阅读11次

    继续延续上一篇 MonkeyLei:Retrofit+Rxjava-以自己的方式重头开始-入门篇 的使用入门,我们继续深入点使用。当然有几点问题:

    1. addConverterFactory里面到底是个什么喵

    2. addCallAdapterFactory到底又是什么,有什么用?再没有用Rxjava的时候,目前都没有用到这个.....(这个下一篇看看怎么用妮...今天就暂时喵一喵Converter

    1.1. addConverterFactory里面的->responseBodyConverter/requestBodyConverter怎么玩?(其实目前从方法名称看,一个是响应的结果转换,一个是请求体的转换)

    1-1 其实上一篇我们就有个 ToStringConverterFactory.java 的转换器,搬砖了一两个项目了,应该还是能看个123。 里面主要就是响应结果做了一个中转处理(其中requestBodyConverter都没有用到,因为我们直接就是get请求,没有涉及到@Body 参数):

    image

    什么情况下会触发requestBodyConverter呢?我们试试Post方法,然后随便给一个@Body参数 看:

        // 添加ToStringConverterFactory解析器 - 返回字符串结果【本质:ResponseBody value -> value.string()】
        // 额外添加一个Body参数,触发requestBodyConverter回调; @GET/@DELTE这些不支持Body
        // @Body不能与@FormUrlEncoded共用,否则报错;而@Field偏偏是提交表单使用的,需要@FormUrlEncoded,So得出@Body和@Field也是不能同时使用的
        @POST("users/{user}/repos")
        fun listReposString(@Path("user") user: String, @Body data: String): Call<String>
    
    image

    然后 ToStringConverterFactory.java 里面打印下日志:

    package com.hl.rxnettest;
    
    import android.util.Log;
    
    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 {
                        Log.e("test", "responseBodyConverter");
                        return value.string();
                    }
                };
            }
            return null;
        }
    
        @Override public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
                                                                        Annotation[] methodAnnotations, Retrofit retrofit) {
            Log.e("test", type.toString());
            if (String.class.equals(type)) {
                return new Converter<String, RequestBody>() {
                    @Override
                    public RequestBody convert(String value) throws IOException {
                        Log.e("test", "requestBodyConverter convert value=" + value);
                        return RequestBody.create(MEDIA_TYPE, value);
                    }
                };
            }
            return null;
        }
    }
    
    
    image

    然后基于上一篇的工程调用下:

            // 方式1: 创建网络请求接口实例 - 返回字符串结果
            // var reposString = gitHubService.listReposString("FanChael")
            var reposString = gitHubService.listReposString("FanChael", "s-requestBodyConverter")
            reposString.enqueue(object : Callback<String>{ // object的作用是调用内部匿名类
                override fun onResponse(call: Call<String>, response: Response<String>){
                    if (response.isSuccessful) {
                        // response.body() -> String
                        // Log.e("reposString-onResponse", response.body())
                    } else {
                        Log.e("reposString-onResponse", "请求错误了!")
                    }
                }
                override fun onFailure(call: Call<String>, t: Throwable){
                    Log.e("reposString-onFailure", t.message)
                }
            })
    

    然后就可以发现,我们requestBodyConverter回调就会走了。

    image

    有时候我们需要提前对请求的数据进行加密或者其他处理,同时请求的数据也需要解密之类的操作,这个时候我们就需要重写Converter.Factory的两个Converter方法,所以我们需要知道如何进行加密数据的处理,传递。

    这里给网友的两种方式,不太一样... Retrofit 之加密和解密 https://blog.csdn.net/zr940326/article/details/51549310

    这样的话,基本上大概就知道怎么利用回调做一些预处理了。官方API也是有的啦:Retrofit 2.3.0 API

    image

    OK!到这里有点了解了。。。。

    **1-2 **这样我们就大概知道这个Convert到底做了啥。其实就是一个请求或者响应的预处理,我们想要拿到结果是一个对象,字符串,我们就需要定义一个转换器来实现,否则我们只能拿到原始的ResponseBody,自己进行处理!阿里的FastJson,小萌新再用,性能貌似不错https://blog.csdn.net/qq_29384639/article/details/81661029 -> 只是一个说法而已!Gson用户还蛮多的,fastjson使用可能比较方便,Gson官方,相对有些地方原始些罢了!

    1-3 Now,我们可以跟踪下GsonConverterFactory的源码先看个大概....

    1.3.1 从头开始跳转过去,先看设置的地方

    image

    GsonConverterFactory.java

    如下, 然后到create函数,主要是创建了一个Gson(我们有时候会自己创建Gson解析器来用,所以不陌生吧)

    image

    其中就肯定有我们的响应和请求的回调了。。。你看网上的很多加密,解密,自定义转换器,基本都是这样的结构。或者都是基于这个进行了定制,增加了自己的一些逻辑!

    image

    GsonResponseBodyConverter.java

    /*
     * Copyright (C) 2015 Square, Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package retrofit2.converter.gson;
    
    import com.google.gson.Gson;
    import com.google.gson.JsonIOException;
    import com.google.gson.TypeAdapter;
    import com.google.gson.stream.JsonReader;
    import com.google.gson.stream.JsonToken;
    import java.io.IOException;
    import okhttp3.ResponseBody;
    import retrofit2.Converter;
    
    final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
      private final Gson gson;
      private final TypeAdapter<T> adapter;
    
      GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
      }
    
      @Override public T convert(ResponseBody value) throws IOException {
        JsonReader jsonReader = gson.newJsonReader(value.charStream());
        try {
          T result = adapter.read(jsonReader);
          if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
            throw new JsonIOException("JSON document was not fully consumed.");
          }
          return result;
        } finally {
          value.close();
        }
      }
    }
    
    

    GsonRequestBodyConverter.java

    /*
     * Copyright (C) 2015 Square, Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package retrofit2.converter.gson;
    
    import com.google.gson.Gson;
    import com.google.gson.TypeAdapter;
    import com.google.gson.stream.JsonWriter;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.nio.charset.Charset;
    import okhttp3.MediaType;
    import okhttp3.RequestBody;
    import okio.Buffer;
    import retrofit2.Converter;
    
    final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
      private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
      private static final Charset UTF_8 = Charset.forName("UTF-8");
    
      private final Gson gson;
      private final TypeAdapter<T> adapter;
    
      GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
      }
    
      @Override public RequestBody convert(T value) throws IOException {
        Buffer buffer = new Buffer();
        Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
        JsonWriter jsonWriter = gson.newJsonWriter(writer);
        adapter.write(jsonWriter, value);
        jsonWriter.close();
        return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
      }
    }
    
    

    上面两个转换器的具体实现类,看不懂,呜呜。。。看来需要冷静了(后面研究下妮)。。。可以先看下网友怎么自定义,然后添加加密解密的:

    Retrofit 之加密和解密 https://blog.csdn.net/zr940326/article/details/51549310

    实现的关键:可以把GsonConverterFactory先搬过来,然后再关键的地方修改。比如这篇关于如何统一处理响应的codeRetrofit自定义GsonConverter处理所有请求错误情况 【基本就是改造的GsonConverterFactory,建议我们也这样做】,然后返回ApiException,最后到BaseSubscriber的onError中进行细节处理。就像小萌新之前封装的BaseSubscribers一样。。。如下,网友的处理:

    image

    **自我说下 **上面关键的就是请求和响应的Type的处理,就是泛型的GsonResponseBodyConverter<T>、TypeAdapter<T>的处理,如何应对到我们请求的类型以及响应的类型。。。。

    如果不考虑这些,我们都可以写死实现,比如:

    Repo.kt

    package com.hl.rxnettest
    
    class Repo{
        var id: Long = -1
        public var name: String? = null
        var full_name: String? = null
        // class owner - not need
        var html_url: String? = null
        var description: String? = null
    }
    

    MGsonConverterFactory.java

     package com.hl.rxnettest;
    
    import android.support.annotation.Nullable;
    import android.util.Base64;
    
    import com.google.gson.Gson;
    import com.google.gson.reflect.TypeToken;
    
    import java.io.IOException;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Type;
    import java.util.List;
    
    import okhttp3.ResponseBody;
    import retrofit2.Converter;
    import retrofit2.Retrofit;
    
    public class MGsonConverterFactory extends Converter.Factory {
        private final Gson gson;
    
        public MGsonConverterFactory(Gson gson) {
            this.gson = gson;
        }
    
        @Nullable
        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
            return new Converter<ResponseBody, List<Repo>>() {
                @Nullable
                @Override
                public List<Repo> convert(ResponseBody value) throws IOException {
                    // 获取返回的字符串结果
                    String result = value.string();
                    // 用Gson转换为对象列表 - 这里展示 Converter.FactoryJson转换数据到对象的过程
                    // -至于如何做到所有的对象,对象列表的转换,那就需要进一步去实现,同时这个type对应的对象的类型该如何运用还得进一步深入解读
                    List<Repo> repoList = gson.fromJson(result, new TypeToken<List<Repo>>(){}.getType());
                    // 返回转换后的对象列表 - GsonConverterFactory.java源码感觉有点难妮,自己太忒么菜了!
                    return repoList;
                }
            };
        }
    }
    
    

    然后使用一把

     @GET("users/{user}/repos")
     fun listRepos(@Path("user") user: String): Call<List<Repo>>
    
     .addConverterFactory(MGsonConverterFactory(Gson()))
    // 方式2: 创建网络请求接口实例 - Gson解析后返回对象列表
            var repos = gitHubService.listRepos("FanChael")
            // 发起请求,UI线程需要异步请求
            repos.enqueue(object : Callback<List<Repo>>{ // object的作用是调用内部匿名类
                override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo>>){
                    if (response.isSuccessful) {
                        // response.body() -> List<Repo>
                        Log.e("repos-onResponse", response.body()?.get(0)?.name)
                    } else {
                        Log.e("repos-onResponse", "请求错误了!")
                    }
                }
                override fun onFailure(call: Call<List<Repo>>, t: Throwable){
                    Log.e("repos-onFailure", t.message)
                }
            })
    

    But,我们不能这样写死,那样就玩球了呀。。。必须要适应所有的类型。我们这样实践主要是为了加深对这个过程的记忆理解,哪怕先肌肉理解也行呀!

    Converter了解先到这。还得继续加深,然后加强理解和拜读......下一篇先了解下CallAdapter,官方api会下拜读一下。争取Retrofit过程都有所熟悉。然后再深入转换部分。。。注解,反射,泛型等。。。。。

    https://github.com/FanChael/RxNet

    加油,菜鸟的我们!

    相关文章

      网友评论

        本文标题:Retrofit+Rxjava-ConverterFactory

        本文链接:https://www.haomeiwen.com/subject/kfhvlctx.html