相信android的同学们应该都对retrofit不陌生,它是由美国Square公司开源的一个用于处理网络请求的框架,该公司还推出很多应用非常广泛的框架,如:okhttp,dagger,picasso等。

仓库地址:https://github.com/square/retrofit

使用方式

retrofit有别与传统的网络框架(准确的说retrofit也不是一个网络框架,它只是对okhttp做了封装,相当于是个工具库),retrofit使用了注解的方式,非常简洁优雅的完成了网络接口的封装。

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

如上,将一个http请求转换为一个java接口。然后构建Retrofit对象:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .validateEagerly(true)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

得到接口对象,发起请求:

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");

call.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> 
              response) {

            }

            @Override
            public void onFailure(Call<List<Repo>> call, Throwable t) {

            }
        });

这就完成了一个http请求,如果对使用上还有不熟悉的可以查看官方文档。这里就不过多的讨论使用方式以及一些注解含义了。

 

原理解析

那么retrofit到底做了什么,我们先从retrofit的构建开始分析。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .validateEagerly(true)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();

老司机看了这段代码应该就知道,retrofit的构建采用了建造者模式,所谓建造者模式就是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

public Builder() {
      this(Platform.get());
    }

最后我们会调用到:

private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("org.robovm.apple.foundation.NSObject");
      return new IOS();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }

这里其实就是判定我们当前程序的运行环境,是android,java8还是IOS,当然我们都是基于android平台来分析的。

static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      return new ExecutorCallAdapterFactory(callbackExecutor);
    }

    static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }

可以看出里面封装了handler,所以在android平台就是通过handler转到了主线程。

前面提到Retrofit的构建是通过建造者模式来的,我们先来看下他的内部类Builder:

public static final class Builder {
    private Platform platform;
    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private List<Converter.Factory> converterFactories = new ArrayList<>();
    private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private Executor callbackExecutor;
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
    }

    public Builder() {
      this(Platform.get());
    }

Platform前面已经大致分析了下,代表运行平台。

看到第三行我们就可以猜到Retrofit其实是使用Okhttp来作为网络请求框架的。

baseUrl顾名思义,将我们传入的url转换成为Okhttp框架中的HttpUrl。

converterFactories,在上例构建中我们有调用addConverterFactory,其中传入的参数GsonConverterFactory.create()就是被存储在converterFactories,主要用于将服务端返回的数据转换成客户端使用的model对象,如通过Gson解析。

adapterFactories用来存放网络适配器,默认是ExecutorCallAdapterFactory,同时我们可以使用rxjava,使用Observable来更好的处理网络请求。

callbackExecutor就是执行的线程池,默认实现是MainThreadExecutor。

validateEagerly用于提前对注解进行解析。之后会分析到。

 

看看Build()

在Retrofit构建的最后一步调用了build,主要代码:

public Retrofit build() {
      ...
      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }

      ...

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

从这里我们也可以发现,创建了一个OkHttpClient,说明了Retrofit就是封装了Okhttp的。最后一行返回创建Retrofit并返回。

Retrofit是怎么处理注解的呢?

主要是通过retrofit.create这个方法,非常重要,他帮助我们生成了接口实现类。那么他到底做了什么呢?

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {//注释1
      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, 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 = loadServiceMethod(method);//注释2
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);//注释3
            return serviceMethod.callAdapter.adapt(okHttpCall);//注释4
          }
        });
  }

原来他是使用了动态代理的方式来实现的,这也是Retrofit的核心之一,也是Retrofit精妙的地方。看注释1,就是在Retrofit构建的时候可以传递的一个参数,上面提过一嘴,用于提前解析,我们可以看到当判断为true的时候就会执行loadServiceMethod(),否则的话就不执行,直到注释2处再执行。

重点关注注释2,3,4三行代码。

ServiceMethod的生成

注释2调用了loadServiceMethod来生成ServiceMethod

ServiceMethod loadServiceMethod(Method method) {
    ServiceMethod result;
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }


serviceMethodCache为一个Map对象: Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>()


逻辑比较简单,以method为key,ServiceMethod为value,先从map中取,如果没有再重新new一个对象,放入map中并返回。这里主要看ServiceMethod的创建:ServiceMethod.Builder(this, method).build();

public Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
  • 获取retrofit实例
  • 接口方法method
  • methodAnnotations,这里一般是指http的请求方式,如GET,POST,就是我们在方法上面添加的注解
  • parameterTypes为参数类型
  • parameterAnnotationsArray网络请求参数注解

再看build():

public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      ...
      responseConverter = createResponseConverter();

      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];
        if (parameterAnnotations == null) {
          throw parameterError(p, "No Retrofit annotation found.");
        }

        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }

     ...

      return new ServiceMethod<>(this);
    }

build方法内容比较多,已经删除了一些不重要的内容。首先获得了callAdapter,responseConverter等,接着调用parseMethodAnnotation()

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);
      }
...
}

可以发现这里是处理Http请求方法的解析。

接着调用parseParameter(p, parameterType, parameterAnnotations),进行参数解析

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;
    }

这个方法比较简单,主要是调用parseParameterAnnotation来得到ParameterHandler<?> annotationAction对象,我们主要看parseParameterAnnotation()

if (annotation instanceof Path) {
        if (gotQuery) {
          throw parameterError(p, "A @Path parameter must not come after a @Query.");
        }
        if (gotUrl) {
          throw parameterError(p, "@Path parameters may not be used with @Url.");
        }
        if (relativeUrl == null) {
          throw parameterError(p, "@Path can only be used with relative url on @%s", httpMethod);
        }
        gotPath = true;

        Path path = (Path) annotation;
        String name = path.value();
        validatePathName(p, name);

        Converter<?, String> converter = retrofit.stringConverter(type, annotations);
        return new ParameterHandler.Path<>(name, converter, path.encoded());

      }

这个方法很长,但是逻辑比较相似,通过if else遍历各个参数注解,进行相应的处理,我这边就截取了其中的一个if语句。比较简单,拿到注解的name,然后通过正则判断是否合法,再构建一个Converter<?, String>对象,我们看下这对象是怎么构建的

public <T> Converter<T, String> stringConverter(Type type, Annotation[] annotations) {
    checkNotNull(type, "type == null");
    checkNotNull(annotations, "annotations == null");

    for (int i = 0, count = converterFactories.size(); i < count; i++) {
      Converter<?, String> converter =
          converterFactories.get(i).stringConverter(type, annotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<T, String>) converter;
      }
    }

    // Nothing matched. Resort to default converter which just calls toString().
    //noinspection unchecked
    return (Converter<T, String>) BuiltInConverters.ToStringConverter.INSTANCE;
  }

可以看到他是通过遍历converterFactories来得到对应的converter,也就是我们在构建Retrofit的时候通过addConverterFactory传递进来的对象。如果没有设置,会取BuiltInConverters.ToStringConverter.INSTANCE,BuiltInConverters是框架内置的转换器。

方法最后再构建一个Path对象(我们这边截取的是path代码块)。就这样通过上述过程,relativeUrl就拼接完成了。

由此,ServiceMethod的创建基本也分析完了

 

OkHttpCall的创建

生成好ServiceMethod之后,将生成好的serviceMethod用于OkHttpCall的参数生成对应的OkHttpCall,OkHttpCall是Retrofit中的call接口的实现类。包含了具体的同步execute()与异步enqueue()操作。

@Override public Response<T> execute() throws IOException {
    okhttp3.Call call;

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

      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else {
          throw (RuntimeException) creationFailure;
        }
      }

      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }

    return parseResponse(call.execute());
  }

可以发现,到这里Retrofit就把网络请求相关的操作直接扔给了Okhttp来处理。

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

   ...
    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      T body = serviceMethod.toResponse(catchingBody);//注释1
      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;
    }
  }

在注释1处,将服务端返回的数据做处理

T toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

很明显,就是调用我们之前所配置的数据转换器responseConverter。

 

serviceMethod.callAdapter.adapt

注释3处就是返回需要被操作的Call对象,用于调用端发起请求。

 

总结

Retrofit的大致分析就是这些,其中还有些细节没有讲到,感兴趣的可以自己查看源码。我这边分析了一个大体的框架。可以发现Retrofit设计还是非常精妙的,使用了大量的设计模式,值得大家品味。