Android技能树 — 网络小结(7)之 Retrofit源码详细解析

栏目: IOS · Android · 发布时间: 5年前

内容简介:介于自己的网络方面知识烂的一塌糊涂,所以准备写相关网络的文章,但是考虑全部写在一篇太长了,所以分开写,希望大家能仔细看,最好可以指出我的错误,让我也能纠正。

哈哈,其实写的还是很水,各位原谅我O(∩_∩)O。

介于自己的网络方面知识烂的一塌糊涂,所以准备写相关网络的文章,但是考虑全部写在一篇太长了,所以分开写,希望大家能仔细看,最好可以指出我的错误,让我也能纠正。

1.讲解相关的整个网络体系结构:

网络体系结构小结

2.讲解相关网络的重要知识点,比如很多人都听过相关网络方面的名词,但是仅限于听过而已,什么tcp ,udp ,socket ,websocket, http ,https ,然后webservice是啥,跟websocket很像,socket和websocket啥关系长的也很像,session,token,cookie又是啥。

相关网络知识点小结-TCP/UDP

相关网络知识点小结- http/https

相关网络知识点小结- socket/websocket/webservice

相关网络知识点小结- cookie/session/token(待写)

3.相关的第三方框架的源码解析,毕竟现在面试个大点的公司,okhttp和retrofit源码是必问的。

okhttp源码解析

Retrofit源码解析

正文

因为我平时使用的都是Rxjava2 + Retrofit ,所以我相关的源码解析都是配合RxJava来的,而不是Call返回对象。

读本文的我推荐大家最好对OKHttp源码有所了解,再来看本文,因为Retrofit内部还是通过OkHttp发出网络请求。大家也可以看我前面写的: Android技能树 — 网络小结之 OkHttp超超超超超超超详细解析 ,同时本文不会再去教大家Retrofit的基础使用,如果要看一些简单使用,可以看下面的一些推荐博客:

Android Retrofit 2.0 的详细 使用攻略(含实例讲解)

Android:Retrofit 结合 RxJava的优雅使用(含实例教程)

我们先上一张别的大佬博客中的一张图:

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

这个图画的很好,但是这个图更多的是从大局观来看,所以如果对于源码不是有一些基础了解的话,看这个图很容易就忘记。

看过我的Okhttp源码分析的文章: Android技能树 — 网络小结之 OkHttp超超超超超超超详细解析 ,我们文中的Okhttp流程图就是跟着源码一步步来画的。我更喜欢是跟着源码一步步来画流程图(PS:其实是我水平太差了,无法一下子总结处第三方库的各种 设计模式 的使用),所以Retrofit我也画了下面这个图:

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

而等会我们分析完这个跟着源码分析的流程图后,再回头看上面的别人博客中的总结的Retrofit结构图,就会很简单了。

首先我们来确定总体大纲:

我们知道我们的目标是要发起一次网络请求,他有这么几步:

  1. 告诉它一些基本信息,比如url地址,网络请求方式(get、post、...等),请求参数值。然后拼装成一个标准的网络Request请求的格式发出去。所以这里有二步动作:1.先解析我们写的参数,2.再解析完后拼装成标准的网络Request请求格式
  2. 发出请求后,接收到了后台的Response返回结果,我们要把Resonse转换成我们想要的返回结果。但是我们写的想要的返回结果又有二大关键地方,我们平常的返回结果可能是 X <Y> ,我们先来看外面的 X的类型 ,比如我们常见的返回结果是 Call<Y> 和 Observable<Y> ,所以我们在转换的时候一是要考虑最外面的那个返回类型的转换。另外一个是 Y的类型 ,也就是里面我们具体写的Bean对象,比如我们直接返回字符串,那可能就是 Observable<String> ,又或者是自己定义的xxxBean对象,那就是 Observable<xxxBean> 。所以我们要有二类转换:1.外层的结果类型,比如Call或者Observable等,2.是泛型里面填的具体的Bean对象类型

所以我们总结起来就需要四步:

  1. 解析并拿到我们写的一些参数(url,请求方式(post/get),请求参数......)
  2. 根据我们写的参数,拼成一个网络请求Request,去帮我们发起请求。
  3. Response如何转换成Call或者Observable等返回类型,和第3步中的Bean对象拼成了Call《Bean》或者Observable《Bean》
  4. Response如何转换成我们所需要的具体的Bean对象。

没错,下次别人问你,你就心里有数了,到底Retrofit做了什么内容,你就跟别人说很简单啦,大致做了上面四步,逼格一下子提高了。。

1. 创建Retrofit对象

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

我这里直接先把创建Retrofit的对象的代码写上:

Retrofit retrofit = new Retrofit.Builder()
    .client(new OkHttpClient())
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl("https://xxxx.com/")
    .build();
复制代码

不要问创建Retrofit的各自的方法是干嘛的,我们后面会一步步讲解。

2. 如何解析并拿到我们写的参数

我们知道我们平常是这样写的:

我们随便写一个常见的获取某个用户的个人信息接口来说明:

InfoApi.java:
interface InfoApi{
    @GET("userinfo.do")
    Observable<UserBean> getInfo(@Query("name") String nameStr);
}
复制代码

那我们要拿到:

  1. path值:上面创建Retrofit时候传入的baseUrl + userinfo.do = "https://xxxx.com/userinfo.do"
  2. 网络请求的方式: GET请求
  3. 发送的参数query : name=nameStr

最终我们发现是GET请求,所以这么拼在一起: path + "?" + query = http://xxxx/userinfo.do?name=nameStr

所以我们来看如何一步步拿到相关参数:

我们知道上面写的 InfoApi.java 是要被retrofit加载进去的:

retrofit.create(InfoApi.class);
复制代码

所以我们要来看 create 方法的具体操作前,我们先来了解一下基础知识,那就是代理模式,如果知道代理模式的,直接可以忽略此处,直接往下看。

2.1 create方法:

在看create代码之间,我们要先学会代理模式相关知识

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

本来也想一步步长篇大论的写下,但是后来看到一篇不错的文章,写的挺仔细的: java动态代理实现与原理详细分析 ,希望大家能仔细看完,在看下面的内容。

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

我们点进去查看具体的代码:

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (this.validateEagerly) {
        this.eagerlyValidateMethods(service);
    }

    
    //'使用了代理模式'
    return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
        private final Platform platform = Platform.get();

        public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
            } else if (this.platform.isDefaultMethod(method)) {
                return this.platform.invokeDefaultMethod(method, service, proxy, args);
            } else {
                
                //'我们可以看到我们写的接口里面的的method传入到了loadServiceMethod方法里面,从而得到了我们定义在method上面的相关参数信息。'
                ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
                
                //'我们传入的方法的参数args和上面获得的ServiceMethod,一起传入OkHttpCall构造函数中,得到OkHttpCall对象'
                OkHttpCall<Object> okHttpCall = new OkHttpCall(serviceMethod, args);
                
                return serviceMethod.adapt(okHttpCall);
            }
        }
    });
}
复制代码

我们可以看到我们调用的 getInfo 这个method方法传入了:

ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
复制代码

我们进去查看:

ServiceMethod<?, ?> loadServiceMethod(Method method) {

//'从缓存中去读'
ServiceMethod<?, ?> result = (ServiceMethod)this.serviceMethodCache.get(method);
    if (result != null) {
        return result;
    } else {
        Map var3 = this.serviceMethodCache;
        synchronized(this.serviceMethodCache) {
            result = (ServiceMethod)this.serviceMethodCache.get(method);
            if (result == null) {
                //'如果缓存中没有,则新建'
                result = (new retrofit2.ServiceMethod.Builder(this, method)).build();
                //'新建完后再放入缓存中'
                this.serviceMethodCache.put(method, result);
            }

            return result;
        }
    }
}
复制代码

我们可以看到新建的方法:

(new retrofit2.ServiceMethod.Builder(this, method)).build();
复制代码

我们来看ServiceMethod类下的Builder的构造函数:

Builder(Retrofit retrofit, Method method) {
    this.retrofit = retrofit;
    this.method = method;
    //'Java特有的方法,可以获取 Java 方法上面的注解标识,比如:@POST,@GET'
    this.methodAnnotations = method.getAnnotations();
    //'获取方法参数里面定义的参数类型,比如:String,boolean'
    this.parameterTypes = method.getGenericParameterTypes();
    //'获取方法里面的注解标识,比如:@Query,@Path'
    this.parameterAnnotationsArray = method.getParameterAnnotations();
}
复制代码

是不是一下子就知道了,原来是通过这样的方式拿到了我们写在方法上面的一些参数值,如果还不清楚Method的这几个方法,可以看下面的相关链接:

Java获取类、方法、属性上的注解

java.lang.reflect.Method.getGenericParameterTypes()方法示例.

使用反射获得参数列表里的注解getParameterAnnotations.

我们创建ServiceMethod因为是使用的Builder模式,所以最终要调用build()方法来创建实例:

public ServiceMethod build() {
      //'创建了CallAdapter对象,具体干嘛用的,具体后面会讲解'
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + “ is not a valid response body type. Did you mean ResponseBody?”);
      }
      
      //'创建了ResponseConverter对象,具体后面会讲解'
      responseConverter = createResponseConverter();
    
      //'对于我们写的接口请求方法的方法上面的注解进行相关判断,'
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      //'因为进行方法上面注解的解析了,所以httpMethod的也就相应的被赋值了,
      如果为空,就说明你写的请求接口方法没有写@GET等,就会抛出异常'
      if (httpMethod == null) {
        throw methodError(“HTTP method annotation is required (e.g., @GET, @POST, etc.).”);
      }
      
      //'因为上面解析了,所以比如我们发现是@GET请求,这时候hasBody会是false,如果你还用了Multipart注解,就会报错了,他要求是要有request body的,@GET请求是不能使用Multipart的'
      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              “Multipart can only be specified on HTTP methods with request body (e.g., @POST).”);
        }
        
        //'同上,表单提交是一定要求有request body的'
        if (isFormEncoded) {
          throw methodError(“FormUrlEncoded can only be specified on HTTP methods with 
              + request body (e.g., @POST).”);
        }
      }
        
      
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      
      //'遍历我们获取的方法里面的注解集合,比如@Query,@Path等'
      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.”);
        }
        
        //'然后对我们写的方法内部参数注解进行判断,看写的是否正确等
        这里的判断很长,比如如果你用的是注解@Body,那么先判断你是否用了方法上面的@FormEncode注解或者@Multipart注解,
        不然就报错,然后因为我们填的参数是对象了,所以内部需要通过RequestBodyConverter来进行转换,把我们传的对象,变成了RequestBody对象。
        具体很多很多判断,各种注解的判断我都不一一讲了,大家只要进去看方法详细代码就可以了。'
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

      if (relativeUrl == null && !gotUrl) {
        throw methodError(“Missing either @%s URL or @Url parameter.”, httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError(“Non-body HTTP method cannot contain @Body.”);
      }
      if (isFormEncoded && !gotField) {
        throw methodError(“Form-encoded method must contain at least one @Field.”);
      }
      if (isMultipart && !gotPart) {
        throw methodError(“Multipart method must contain at least one @Part.”);
      }

      return new ServiceMethod<>(this);
}
复制代码

好,我们已经成功拿到了我们的方法中的红色框出来的部分,绿色的部分我们还没有获取。

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

而代理模式的invoke方法里面的参数 @Nullable Object[] args,就是我们具体传入的参数,比如我这么写:

getInfo("青蛙要fly");
复制代码

args里面就有了我们传入的 "青蛙要fly" 字符串。这样我们是不是就获取了上面的其中一个绿色框nameStr的内容了。

我们拿到包含了这些红色框参数的ServiceMethod对象后,加上我们传入的绿色的框的 nameStr的具体的值 ,我们已经可以进行网络Request请求的所必要的参数了 (另外一个绿色的框只是用来最后网络请求成功后拿到的Response进行转换,所以这时候不知道都不影响Request请求)

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

我们可以看到我们获得到的信息,又用来生成了OkHttpCall对象,然后调用了 serviceMethod.adapt(okHttpCall); 方法。

那我们可以看到create接下去已经没有其他代码了,所以 serviceMethod.adapt(okHttpCall); 肯定会帮我们用刚才拿到的已知参数,帮我们拼成Request,完成一次网络请求。

3.根据我们写的参数,拼成Request请求

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

我们上面已经说到了进入了 serviceMethod.adapt(okHttpCall); 方法了,我们点进去查看:

T adapt(Call<R> call) {
    return callAdapter.adapt(call);
}
复制代码

我们可以看到是调用了callAdapter类的adapt方法。那这个callAdapter对象又是什么呢?

还记不记得我们第一大步:创建Retrofit对象时候的代码:

Retrofit retrofit = new Retrofit.Builder()
    .client(new OkHttpClient())
    //'这里传入了CallAdapterFactory,而Factory类是用来创建具体的CallAdapter对象的工厂类'
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl("https://xxxx.com/")
    .build();
复制代码

所以本文中我们使用的CallAdapter是RxJava2CallAdapterFactory创建的,我们先来看ServiceMethod里面创建CallAdapter的方法:

private CallAdapter<T, R> createCallAdapter() {

    //'我们上面的接口请求方法绿色框里面的返回类型还没有拿到的,终于在这里拿到了'
    Type returnType = method.getGenericReturnType();
    
    //'如果方法的返回结果包含了泛型表达式、泛型、泛型数组,就抛出异常'
    if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
        “Method return type must not include a type variable or wildcard: %s”, returnType);
    }
    
    //'如果方法的返回结果是void,则抛出异常'
    if (returnType == void.class) {
        throw methodError(“Service methods cannot return void.”);
    }
    
    //'我们前面提过的,获取方法上的注解,比如@GET等'
    Annotation[] annotations = method.getAnnotations();
    try {
       
       //'拿着我们的接口请求方法的返回对象及方法上的注解信息,'
       //'去通过Retrofit类的callAdapter类去生成一个CallAdapter对象'
       return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
       throw methodError(e, “Unable to create call adapter for %s”, returnType);
    }
}
复制代码

而Retrofit类中的这个callAdapter方法,我们不看都知道,通过我们前面创建Retrofit对象时候传入的addCallAdapterFactory的工厂类来创建具体的CallAdapter,当然我们具体还是要具体代码一步步来看过程。

我们在调用addCallAdapterFactory加入我们的 RxJava2CallAdapterFactory.create() ,所以先来看下addCallAdapterFactory方法做了什么:

public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
  callAdapterFactories.add(checkNotNull(factory, "factory == null"));
  return this;
}
复制代码

我们可以简单的看到,就是把我们的Factory工厂类对象加入到 private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(); 这个List队列中而已。

那这个队列到底都加了哪些工厂类的,如果我在创建Retrofit对象时候不调用 addCallAdapterFactory 方法,难道这个队列就是空的????那又怎么去生成CallAdapter对象?

首先肯定要加入我们自己传入的Factory,有可能一个,也可能传入多个:

Retrofit retrofit = new Retrofit.Builder()
    ........
    
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addCallAdapterFactory(xxxxxxCallAdapterFactory.create())
    .addCallAdapterFactory(yyyyyyCallAdapterFactory.create())
    ........
    ........
    .build();

复制代码

但是为了防止我们建立Retrofit对象时候不调用addCallAdapterFactory传入自己的Factory,所以本身这个队列还会加入默认的Factory:

//'看名字就知道,加入平台的默认的CallAdapterFactory(有java8 和 Android Platform,我们这里肯定是Android)'
callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
复制代码
Android技能树 — 网络小结(7)之 Retrofit源码详细解析

当然这个ExecutorCallAdapterFactory肯定是继承了CallAdapter.Factory:

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

我们已经知道了我们的CallAdapterFactory队列里面包含了哪些工厂类了。接下来我们再来具体的Retrofit的callAdapter的方法:

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    //'因为可能有多个CallAdapterFactory工厂类,所以要每个工厂类都去试一下,有一个成功就直接返回了'
    return nextCallAdapter(null, returnType, annotations);
}



public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");
    
    
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    
    //'循环遍历所有的CallAdapterFactory,然后哪个能成功生成CallAdapter,就直接返回'
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    
    //'如果所有的CallAdapterFctory都不能使用,就拼接字符串,抛出异常'
    StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
        .append(returnType)
        .append(".\n");
    if (skipPast != null) {
      builder.append("  Skipped:");
      for (int i = 0; i < start; i++) {
        builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
}
复制代码

有可能有人会问,为什么CallAdapterFactory有可能生成CallAdapter不成功??还要一个个去遍历?

因为我们同时传入了我们需要返回的对象的类型传入到了CallAdapterFactory中,你说如果你是默认的 ExecutorCallAdapterFactory 工厂类,你却传入了Rxjava的返回相关参数,比如我们例子中的 Observable<UserBean> ,它的代码里面都不认识这种返回类型,怎么帮你去生成对象,而且代码也是加了判断,如果返回类型不是Call类型,直接就退出了。

@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    //'如果不是Call.class,直接退出生成CallAdapter对象'
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    .......
    .......
}
复制代码

所以我们来看下RxJava2CallAdapterFactory里面怎么生成相应的CallAdapter的:

@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    Class<?> rawType = getRawType(returnType);
    
    
    
    //'如果我们的返回类型是Completable,就直接返回RxJava2CallAdapter对象,里面的responseType是void'
    if (rawType == Completable.class) {
      // Completable is not parameterized (which is what the rest of this method deals with) so it
      // can only be created with a single configuration.
      return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false, true, false, false,
          false, true);
    }


    //'判断是否是Flowable或者Single或者Maybe'
    boolean isFlowable = rawType == Flowable.class;
    boolean isSingle = rawType == Single.class;
    boolean isMaybe = rawType == Maybe.class;
    //'如果既不是上面三种又不是Observable类型,直接返回null'
    if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {
      return null;
    }


    boolean isResult = false;
    boolean isBody = false;
    Type responseType;
    
    //'如果不是泛型类的,比如Observable<XXXX> ,则抛异常'
    if (!(returnType instanceof ParameterizedType)) {
      String name = isFlowable ? "Flowable"
          : isSingle ? "Single"
          : isMaybe ? "Maybe" : "Observable";
      throw new IllegalStateException(name + " return type must be parameterized"
          + " as " + name + "<Foo> or " + name + "<? extends Foo>");
    }
    
    //'获取泛型中的具体参数,比如Observable<xxxBean>中的xxxBean的type'
    Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
    //'获取xxxBean具体的Class对象'
    Class<?> rawObservableType = getRawType(observableType);
    
    //'判断我们上面获取的泛型内容(xxxBean)是不是Response'
    if (rawObservableType == Response.class) {
      if (!(observableType instanceof ParameterizedType)) {
        throw new IllegalStateException("Response must be parameterized"
            + " as Response<Foo> or Response<? extends Foo>");
      }
      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      
      //'判断我们上面获取的泛型内容(xxxBean)是不是Result'
    } else if (rawObservableType == Result.class) {
      if (!(observableType instanceof ParameterizedType)) {
        throw new IllegalStateException("Result must be parameterized"
            + " as Result<Foo> or Result<? extends Foo>");
      }
      responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      isResult = true;
    } else {
    
      //'我们平常开发泛型里面填的肯定是自己的Bean对象
      //所以最后走的是这里的代码'
      responseType = observableType;
      //'同时isBody设置为true'
      isBody = true;
    }

    //'生成具体的Rxjava2CallAdapter对象'
    return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
        isSingle, isMaybe, false);
}
复制代码

既然我们CallAdapter对象也建立完了,我们回到最刚开始的地方,还记得我们前面分析的代码是到了 callAdapter.adapt(okHttpCall) (如果忘记的同学,可以重新回头看下)

所以我们现在已经建立的Rxjava2CallAdapter对象了,我们来看下它的adapt方法:

Android技能树 — 网络小结(7)之 Retrofit源码详细解析
@Override public Object adapt(Call<R> call) {
    /**
    '很多人会说这个isAsync,是否异步是哪里设置的,
    其实就是再我们传入Factory对象时候建立的。
    
    Retrofit retrofit = new Retrofit.Builder()
    .client(new OkHttpClient())
    我们看见创建Factory对象,可以是createAsync()或者create()方法二种来创建,从而决定是同步还是异步操作
    .addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
    或者是
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create()
    .build();'
    */
    
     //'我们可以看到上面根据是否异步,建立不同的Observable对象,我们用复杂点的来讲解吧,
     就当我们建立的时候使用的是RxJava2CallAdapterFactory.createAsync()方法,所以拿到的对象是CallEnqueueObservable'
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);
   

    //'因为我们Observable<xxxBean>里面包含的是自己Bean,所以建立的时候isBody = true;'
    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      //'所以我们的Observable为BodyObservable'
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }

    if (scheduler != null) {
      observable = observable.subscribeOn(scheduler);
    }

    if (isFlowable) {
      return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    if (isSingle) {
      return observable.singleOrError();
    }
    if (isMaybe) {
      return observable.singleElement();
    }
    if (isCompletable) {
      return observable.ignoreElements();
    }
    
    //'所以最终返回了BodyObservable<CallEnqueueObservable>'
    return observable;
  }
复制代码

BodyObservable看名字就知道是一个自定义Observable:

final class BodyObservable<T> extends Observable<T> {
  private final Observable<Response<T>> upstream;

  BodyObservable(Observable<Response<T>> upstream) {
    this.upstream = upstream;
  }

  @Override protected void subscribeActual(Observer<? super T> observer) {
  
    //'当有Observer注册我们的Observable的时候,
    其实是我们前面的传入的CallEnqueueObservable去注册了
    一个BodyObserver<我们自己写的Observer>'
    upstream.subscribe(new BodyObserver<T>(observer));
  }

}
复制代码

所以核心还是我们传入的 CallEnqueueObservable 这个Observable,所以最后还是要看这个类的源码:

final class CallEnqueueObservable<T> extends Observable<Response<T>> {
  private final Call<T> originalCall;

  CallEnqueueObservable(Call<T> originalCall) {
    this.originalCall = originalCall;
  }

  @Override protected void subscribeActual(Observer<? super Response<T>> observer) {
    // Since Call is a one-shot type, clone it for each new observer.
    
    
    Call<T> call = originalCall.clone();
    //'我们可以看到简历了一个CallCallback对象,传入了用户写的我们前面创建的OkHttpCall对象和用户写的observer对象'
    CallCallback<T> callback = new CallCallback<>(call, observer);
    observer.onSubscribe(callback);
    //'然后调用了call的enqueue方法,
    因为是OkHttpCall对象,所以我们直接看OkHttpCall对象的enqueue方法即可'
    call.enqueue(callback);
  }

  private static final class CallCallback<T> implements Disposable, Callback<T> {
   .......
   .......
   .......
  }
}


复制代码

OkHttpCall的enqueue方法:

@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
        
          //'创建Okhttp3的Call对象(毕竟最后发起网络请求是Okhttp,也要使用它的Call对象)'
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }
    
    
    //'常规Okhttp的操作,call.enqueue方法发起异步请求,估计大家都看得懂,我就不多介绍了,我们直接看拿到返回的数据处理'
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
        
          //'我们这里成功的拿到了Okhttp3.Response对象,
          所以使用parseResponse方法将rawResponse对象转换成Retrofit的Response对象'
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }

      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }
复制代码

到这里,我们已经成功的发送了网络请求,并且拿到了主句

4. 如何将Resonse转换成最终我们想要的结果对象

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

我们上面可以看到我们是讲OkHttp3.Response对象转换成了Retrofit.Response对象,我们具体来看下:

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {

    //'把Okhttp3.Response中的body部分取出来'
    ResponseBody rawBody = rawResponse.body();

    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();
    
    //'我们就当成功请求回来的,所以code是200'
    
    if (code < 200 || code >= 300) {
      try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
      } finally {
        rawBody.close();
      }
    }

    if (code == 204 || code == 205) {
      rawBody.close();
      return Response.success(null, rawResponse);
    }
    
    
    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
    
      //'核心代码:
      把body部分,通过toResponse方法,变成我们写入的泛型(也就是Observable<xxxBean>这个xxxBean对象'
      T body = serviceMethod.toResponse(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
}
复制代码

所以最终我们发现又回到了serviceMethod里面了,相关的方法调用都在这里面:

R toResponse(ResponseBody body) throws IOException {
   //'可以看到我们通过responseConverter转换器来对body部分进行了转换'
   return responseConverter.convert(body);
}
复制代码

这个responseConverter又是怎么来的呢?我们再回到创建Retrofit对象的地方:

Retrofit retrofit = new Retrofit.Builder()
    .client(new OkHttpClient())
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    
    //'看见了ConverterFactory没有,就是这里传入了我们的转换器对象'
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl("https://xxxx.com/")
    .build();

复制代码

我们来看下具体的代码:

public final class GsonConverterFactory extends Converter.Factory {
    
    //'可以看到默认内部使用的是GSON来进行转换'
    public static GsonConverterFactory create() {
        return create(new Gson());
    }
    
    public static GsonConverterFactory create(Gson gson) {
        return new GsonConverterFactory(gson);
    }
    
    private final Gson gson;

    private GsonConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    }
    
    //'这个方法从名字就可以看出来,是用用来给ResponseBody转换成我们要的对象'
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(final Type type, Annotation[] annotations, Retrofit retrofit) {
        Type newType = new ParameterizedType() {
            @Override
            public Type[] getActualTypeArguments() {
                return new Type[] { type };
            }

            @Override
            public Type getOwnerType() {
                return null;
            }

            @Override
            public Type getRawType() {
                return HttpResult.class;
            }
        };
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(newType));
        
        //'可以看到,用在Response的转换器是叫GsonResponseBodyConverter对象'
        return new GsonResponseBodyConverter<>(adapter);
    }
    
    //'这个名字也可以看出来是把我们传入的对象转换成RequestBody,从而发起请求'
    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
        Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        
        //'可以看到,用在Request的转换器是叫GsonRequestBodyConverter对象'
        return new GsonRequestBodyConverter<>(gson, adapter);
    }
}
复制代码

我们具体来看看 GsonResponseBodyConverter :

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 {
    //'根据传入的ResponseBody得到JsonReader'
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      //'很简单,就是GSON进行相关的JSON解析'
      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();
    }
  }
}
复制代码

这里我们看到了既然解析部分是在这里,是不是我们可以做很多定制化操作,答案当然是Yes,比如我写了个自定义的GsonResponseBodyConverter来进行替换(下面的类就随便写写,大家可以根据自己的需求写自己的自定义转换器):

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, Object> {

    private final TypeAdapter<T> adapter;

    GsonResponseBodyConverter(TypeAdapter<T> adapter) {
        this.adapter = adapter;
    }

    @Override
    public Object convert(ResponseBody value) throws IOException {
        try {
            //'因为我统一的外层对象都是使用的HttpResult,我的代码是这么写的Observable<HttpResult<xxxBean>>'
            HttpResult apiModel = (HttpResult) adapter.fromJson(value.charStream());
            
            //'直接在这里就对统一处理操作'
            if (apiModel.getCode().equals(CompanyHttpCode.TOKEN_NOT_EXIST)) {
                throw new TokenNotExistException();
            } else if (apiModel.getCode().equals(CompanyHttpCode.TOKEN_INVALID)) {
                throw new TokenInvalidException();
            } else if (!apiModel.getCode().equals(CompanyHttpCode.SUCCESS_CODE)) {
                // 特定 API 的错误,在相应的 Subscriber 的 onError 的方法中进行处理
                throw new ApiException();
            } else if (apiModel.getCode().equals(CompanyHttpCode.SUCCESS_CODE) || apiModel.getCode().equals(CompanyHttpCode.SUCCESS_CODE_STR)) {
                return apiModel;
            }

        } finally {
            value.close();
        }
        return null;
    }
}
复制代码

好了,我们已经拿到了相应的 Observable<xxxBean> 里面的xxxBean对象了,我们可以看到:

try {
    //'我们前面讲过,通过这个方法转换的parseResponse(rawResponse);
    把OkHttp3.Response转换成了Retrofit.Response<我们的bean> '
} catch (Throwable e) {
    callFailure(e);
    return;
}

try {
    //'在转换成功后,我们就把具体的response重新通过回调函传回去给CallEnqueueObservable'
    callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
    t.printStackTrace();
}
复制代码

CallEnqueueObservable的onResponse方法:

@Override public void onResponse(Call<T> call, Response<T> response) {
      if (disposed) return;

      try {
        
        //'我们可以看到,Observable调用了observerde的onNext方法把Retrofit.Reponse对象发送了出去'
        observer.onNext(response);
        
        ......
        ......
        ......
}
复制代码

可能有些人就会奇怪了,我们平常使用,明明拿到的就是具体的里面的xxxBean对象,而不是 Response<xxxBean> ,那是因为上面我们提过的,我们的Observer被BodyObserver包了一层:

private static class BodyObserver<R> implements Observer<Response<R>> {
   
    @Override public void onNext(Response<R> response) {
      if (response.isSuccessful()) {
        
        //'最终到我们的Observer的时候,就是Response里面包含了的我们写的xxxBean对象了。'
        observer.onNext(response.body());
      
          
      } else {
        .....
        .....
      }
    }
    
    ......
    ......
  }
复制代码

结语:

所以现在我们再来看代码,是不是已经就能懂中间到底做了什么操作。哈哈:

interface InfoApi{
    @GET("userinfo.do")
    Observable<UserBean> getInfo(@Query("name") String nameStr);
}

Retrofit retrofit = new Retrofit.Builder()
    .client(new OkHttpClient())
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl("https://xxxx.com/")
    .build();


retrofit.create(InfoApi.class)
    .getInfo("青蛙要fly")
    .subscribe(new ResourceObserver<UserBean>() {
        @Override
        public void onNext(UserBean userBean) {
                        
        }

        @Override
        public void onError(Throwable e) {

        }

        @Override
        public void onComplete() {

        }
});

复制代码

然后再回头看这个图片,是不是也看得懂了:

Android技能树 — 网络小结(7)之 Retrofit源码详细解析

不知不觉就写完了,哈哈,可能有些地方不详细或者是写的不好又或者是写错了。可以留言,我更希望的是能指出我哪里写错了,哈哈,这样我也可以纠正错误的知识。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Beginning Google Maps API 3

Beginning Google Maps API 3

Gabriel Svennerberg / Apress / 2010-07-27 / $39.99

This book is about the next generation of the Google Maps API. It will provide the reader with the skills and knowledge necessary to incorporate Google Maps v3 on web pages in both desktop and mobile ......一起来看看 《Beginning Google Maps API 3》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具