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

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

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

哈哈,其实写的还是很水,各位原谅我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源码详细解析

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


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

查看所有标签

猜你喜欢:

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

加密与解密(第4版)

加密与解密(第4版)

段钢 / 电子工业出版社 / 2018-10-1 / 198

《加密与解密(第4版)》以加密与解密为切入点,讲述了软件安全领域的基础知识和技能,如调试技能、逆向分析、加密保护、外壳开发、虚拟机设计等。这些知识彼此联系,读者在掌握这些内容之后,很容易就能在漏洞分析、安全编程、病毒分析、软件保护等领域进行扩展。从就业的角度来说,掌握加密与解密的相关技术,可以提高自身的竞争能力;从个人成长的角度来说,研究软件安全技术有助于掌握一些系统底层知识,是提升职业技能的重要......一起来看看 《加密与解密(第4版)》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

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

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具