简介

Retrofit turns your HTTP API into a Java interface.

Retrofit将HTTP API转换为Java接口

本文基于Retrofit2.9.0版本源码分析!

依赖添加:

com.squareup.retrofit2:retrofit:2.9.0

使用

我们在这以官网的Demo为例,来看下最简单的使用:

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

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build(); ②

GitHubService service = retrofit.create(GitHubService.class); ③

Call<List<Repo>> repos = service.listRepos("octocat"); ④

repos.enqueue() ⑤

总体流程分为四步:

  1. 创建一个接口,内部方法都是接口调用的方法,使用注解的方式添加请求类型、参数和回调数据等;
  2. 构建一个Retrofit对象,并且设置接口的baseUrl
  3. 使用retrofit.create(class)代理出一个我们第一步创建的接口实例;
  4. 使用接口实例执行具体的接口调用,返回Call对象;
  5. 使用call.enqueu()方法执行网络请求。

第一步不用具体分析,只需要知道如果使用注解来添加我们请求内容即可,具体如何解析这些注解会在第三步呈现出来,下面直接看第二步Retrofit的创建过程。

创建Retrofit

Retrofit.Builder().baseUrl(string)

retrofit2.Retrofit.Builder#baseUrl(java.lang.String)

public Builder baseUrl(String baseUrl) {
  Objects.requireNonNull(baseUrl, "baseUrl == null");
  return baseUrl(HttpUrl.get(baseUrl)); ①
}

public Builder baseUrl(HttpUrl baseUrl) {
  Objects.requireNonNull(baseUrl, "baseUrl == null");
  List<String> pathSegments = baseUrl.pathSegments();
  if (!"".equals(pathSegments.get(pathSegments.size() - 1))) { ②
    throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
  }
  this.baseUrl = baseUrl; ③
  return this;
}

Retrofit.Builder().baseUrl(string)一共做了三个操作:

  1. 将传入的url封装成HttpUrl对象;
  2. 判断url是否是以/结尾,不是的话直接抛出异常;
  3. HttpUrl赋值给Builder.baseUrl

Builder.build()

retrofit2.Retrofit.Builder#build

public Retrofit build() {
    // baseUrl不能为空
    if (baseUrl == null) {
      throw new IllegalStateException("Base URL required.");
    } ①
    
    // 获取平台,Java/Android
    Platform platform = Platform.get(); ②

    // 创建OkHttpClient对象
    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
      callFactory = new OkHttpClient();
    } ③

    // 创建回调线程池对象
    Executor callbackExecutor = this.callbackExecutor;
    if (callbackExecutor == null) {
      callbackExecutor = platform.defaultCallbackExecutor();
    } ④

    // 创建适配器列表,并且添加默认的适配器
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    List<? extends CallAdapter.Factory> defaultCallAdapterFactories =
        platform.createDefaultCallAdapterFactories(callbackExecutor);
    callAdapterFactories.addAll(defaultCallAdapterFactories); ⑤

    // 创建默认的转换器列表
    List<? extends Converter.Factory> defaultConverterFactories =
        platform.createDefaultConverterFactories();
    int defaultConverterFactoriesSize = defaultConverterFactories.size();
    List<Converter.Factory> converterFactories =
        new ArrayList<>(1 + this.converterFactories.size() + defaultConverterFactoriesSize);

    // 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());
    converterFactories.addAll(this.converterFactories);
    converterFactories.addAll(defaultConverterFactories); ⑥

    // 创建Retrofit对象
    return new Retrofit(
        callFactory,
        baseUrl,
        unmodifiableList(converterFactories),
        defaultConverterFactoriesSize,
        unmodifiableList(callAdapterFactories),
        defaultCallAdapterFactories.size(),
        callbackExecutor,
        validateEagerly); ⑦
  }
}

Builder.build()方法中主要就是创建或者获取一些重要的对象,一共包括6个对象,下面一个个分析下具体作用:

  1. 第一步判断baseUrl是否为空,为空直接抛异常;
  2. 第二步获取Platform平台对象,因为我们在Android工程中使用,所以获取的是Android21或者Android24其中一个,Platform定义了回调线程池、调用适配器和转换适配器对象等;
  3. 第三步获取回调线程池对象,会从Platform对象中获取默认的线程池;
  4. 第四步获取CallAdapter.Factory列表,并且添加从Platform中获取到的默认CallAdapter.Factory对象。CallAdapter的主要作用就是将我们接口返回的对象转换成想要的类型,典型的就是RxJavaCallAdapter,它就是将默认的Call转换成Observable
  5. 第五步获取Converter.Factory列表,同时也会从Platform中获取默认的Converter.Factory列表。Converter.Factory的主要作用就是将请求结果通过具体的操作转换成想要的类型,典型的就是GsonConverter,它就是将String转换成具体的数据类对象。
  6. 第六步创建Retrofit对象,然后设置上面获取的对象。

Retrofit.cerate()

retrofit2.Retrofit#create

public <T> T create(final Class<T> service) {
  validateServiceInterface(service);
  return (T)
      Proxy.newProxyInstance( ①
          service.getClassLoader(),
          new Class<?>[] {service},
          new InvocationHandler() {
            private final Object[] emptyArgs = new Object[0];

            @Override
            public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                throws Throwable {
              // 判断Service是接口还是类,如果是类直接调用类的方法
              if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
              }
              args = args != null ? args : emptyArgs;
              Platform platform = Platform.get();
              return platform.isDefaultMethod(method)
                  ? platform.invokeDefaultMethod(method, service, proxy, args)
                  : loadServiceMethod(method).invoke(args); ②
            }
          });
}

Retrofit.create(class)方法中可以看出主要的有两步:

  1. 第一步使用动态代理模式,根据传入的class对象代理最终代理出一个实例;
  2. 第二步先判断platform.isDefaultMethod(method),进入Android21类中此方法默认false,所以直接看loadServiceMethod(method)方法即可
    • loadServiceMethod(method)会创建一个ServiceMethod对象;
    • 再调用ServiceMethod.invoke(args)方法,这里传入的接口方法的参数

Retrofit.loadServiceMethod()

retrofit2.Retrofit#loadServiceMethod

ServiceMethod<?> loadServiceMethod(Method method) {
  // 查看是否命中缓存
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    // 加锁并且双重判断是否命中缓存
    result = serviceMethodCache.get(method);
    if (result == null) {
      // 获取新的ServiceMethod对象,并存入Map中
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

loadServiceMethod()方法内部逻辑还是比较简单,先是从Map中获取是否已经存在,不存在再加锁去获取新的ServiceMethod对象,最后放入缓存Map中。具体的获取流程还需要进入ServiceMethod.parseAnnotations(retrofit,method)方法中。

ServiceMethod.parseAnnotations()

retrofit2.ServiceMethod#parseAnnotations

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
  // 创建RequestFactory对象
  RequestFactory requestFactory = 
    RequestFactory.parseAnnotations(retrofit, method); ①
  
  // 获取方法返回类型
  Type returnType = method.getGenericReturnType();
  // 判断返回类型是否能够解析
  if (Utils.hasUnresolvableType(returnType)) {
    throw methodError(
        method,
        "Method return type must not include a type variable or wildcard: %s",
        returnType);
  }
  // 返回类型不能为void类型
  if (returnType == void.class) {
    throw methodError(method, "Service methods cannot return void.");
  } ②
  // 创建HttpServiceMethod对象
  return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory); ③
}

parseAnnotations()方法中主要做了三件事:

  1. 第一件就是创建了RequestFactory对象,这个接下来分析下这个对象作用是什么;
  2. 第二件通过Method获取方法返回值,并判断可用性;
  3. 第三件转到HttpServiceMethod中创建HttpServiceMethod对象,此类是ServiceMethod的子类。

RequestFactory.parseAnnotations(retrofit, method)

retrofit2.RequestFactory#parseAnnotations

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
  return new Builder(retrofit, method).build();
}

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

RequestFactory build() {
  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation); ①
  }
  // httpMethod不能为空,也就是设置的请求方式GET、POST等
  if (httpMethod == null) {
    throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  } ②

  // 判断请求提是否合规
  if (!hasBody) {
    if (isMultipart) {
      throw methodError(
          method,
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError(
          method,
          "FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
    }
  } ③

  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
    parameterHandlers[p] =
        parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
  } ④

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

  return new RequestFactory(this); ⑤
}

parseAnnotations()方法直接调用了内部Builder.build()方法,build()方法中,可以细分为五步:

  1. 第一步通过parseMethodAnnotation()解析方法上注解,解析的内容包括:请求方式、请求头、请求地址、是否Multipart和是否FormUrlEncode等信息,下面会详细分析;
  2. 第二步判断请求方式,如果为空直接抛异常;
  3. 第三步判断MultipartFormUrlEncode注解,如果有请求体的时候,不可以有前面两个注解,否则直接抛异常;
  4. 第四步通过parseParameter解析参数上注解,包括BodyFiledHeaderQuery等信息;
  5. 第五步创建RequestFactory对象,将Builder传入进去。

RequestFactory.parseMethodAnnotation()

retrofit2.RequestFactory.Builder#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);
  } else if (annotation instanceof HEAD) {
    parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
  } else if (annotation instanceof PATCH) {
    parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
  } else if (annotation instanceof POST) {
    parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
  } else if (annotation instanceof PUT) {
    parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
  } else if (annotation instanceof OPTIONS) {
    parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
  } else if (annotation instanceof HTTP) {
    HTTP http = (HTTP) annotation;
    parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
  } else if (annotation instanceof retrofit2.http.Headers) {
    String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
    if (headersToParse.length == 0) {
      throw methodError(method, "@Headers annotation is empty.");
    }
    headers = parseHeaders(headersToParse);
  } else if (annotation instanceof Multipart) {
    if (isFormEncoded) {
      throw methodError(method, "Only one encoding annotation is allowed.");
    }
    isMultipart = true;
  } else if (annotation instanceof FormUrlEncoded) {
    if (isMultipart) {
      throw methodError(method, "Only one encoding annotation is allowed.");
    }
    isFormEncoded = true;
  }
}

parseMethodAnnotation()主要就是解析出方法上的注解,比如是GET还是POST请求,有没有额外添加HEADER,或者是不是MultipartFormUrlEncoded

RequestFactory.parseParameter()

retrofit2.RequestFactory.Builder#parseParameter

private @Nullable ParameterHandler<?> parseParameter(
    int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
  ParameterHandler<?> result = null;
  if (annotations != null) {
    for (Annotation annotation : annotations) {
      ParameterHandler<?> annotationAction =
          parseParameterAnnotation(p, parameterType, annotations, annotation);

      if (annotationAction == null) {
        continue;
      }

      if (result != null) {
        throw parameterError(
            method, p, "Multiple Retrofit annotations found, only one allowed.");
      }

      result = annotationAction;
    }
  }
.....
  return result;
}

parseParameter()具体的解析流程在parseParameterAnnotation()方法中,由于此方法源码过于长,此处就不再贴源码了,里面其实就是根据不同的注解类型来解析成对应的ParameterHandler对象。

具体的类型如下:

  • @Url注解,解析出来对应的是ParameterHandler.RelativeUrl()对象;
  • @Path注解,解析出来对应的是ParameterHandler.Path()对象;
  • @Query注解,解析出来对应的是ParameterHandler.Query()对象;
  • @QueryName注解,解析出来对应的是ParameterHandler.QueryName()对象;
  • @QueryMap注解,解析出来对应的是ParameterHandler.QueryMap()对象;
  • @Header注解,解析出来对应的是ParameterHandler.Header()对象;
  • @HeaderMap注解,解析出来对应的是ParameterHandler.HeaderMap()对象;
  • @Field注解,解析出来对应的是ParameterHandler.Field()对象;
  • @FieldMap注解,解析出来对应的是ParameterHandler.FieldMap()对象;
  • @Part注解,解析出来对应的是ParameterHandler.Part()对象;
  • @PartMap注解,解析出来对应的是ParameterHandler.PartMap()对象;
  • @Body注解,解析出来对应的是ParameterHandler.Body()对象;
  • @Tag注解,解析出来对应的是ParameterHandler.Tag()对象;

这就是所有的方法参数注解类型。

RequestFactory.parseAnnotations()分析完了之后回到HttpServiceMethod.parseAnnotations()

HttpServiceMethod.parseAnnotations()

retrofit2.HttpServiceMethod#parseAnnotations

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
  boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
  boolean continuationWantsResponse = false;
  boolean continuationBodyNullable = false;
  boolean continuationIsUnit = false;

  Annotation[] annotations = method.getAnnotations();
  Type adapterType;
  if (isKotlinSuspendFunction) { ①
    Type[] parameterTypes = method.getGenericParameterTypes();
    Type responseType =
        Utils.getParameterLowerBound(
            0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
    if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
      // Unwrap the actual body type from Response<T>.
      responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
      continuationWantsResponse = true;
    } else {
      continuationIsUnit = Utils.isUnit(responseType);
    }

    adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
    annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
  } else {
    adapterType = method.getGenericReturnType(); ②
  }

  CallAdapter<ResponseT, ReturnT> callAdapter =
      createCallAdapter(retrofit, method, adapterType, annotations); ③
  Type responseType = callAdapter.responseType();
  if (responseType == okhttp3.Response.class) {
    throw methodError(
        method,
        "'"
            + getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
  }
  if (responseType == Response.class) {
    throw methodError(method, "Response must include generic type (e.g., Response<String>)");
  }
  // TODO support Unit for Kotlin?
  if (requestFactory.httpMethod.equals("HEAD")
      && !Void.class.equals(responseType)
      && !Utils.isUnit(responseType)) {
    throw methodError(method, "HEAD method must use Void or Unit as response type.");
  }

  Converter<ResponseBody, ResponseT> responseConverter =
      createResponseConverter(retrofit, method, responseType); ④

  okhttp3.Call.Factory callFactory = retrofit.callFactory; ⑤
  if (!isKotlinSuspendFunction) {
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  } else if (continuationWantsResponse) {
    //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForResponse<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter); ⑥
  } else {
    //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    return (HttpServiceMethod<ResponseT, ReturnT>)
        new SuspendForBody<>(
            requestFactory,
            callFactory,
            responseConverter,
            (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
            continuationBodyNullable,
            continuationIsUnit); ⑦
  }
}

这里面的逻辑还是略微比较多的,逐一看下:

  1. 第一步,判断是否是Kotlin的挂起函数,然后通过getGenericParameterTypes获取方法的返回类型;
  2. 第二步,如果不是Kotlin的挂起函数,直接通过getGenericReturnType获取方法的返回类型,这里的返回类型如果是泛型的话有可能拿不到具体的泛型值;
  3. 第三步创建CallAdapter,这里假设我们没有设置任何额外的CallAdapter,会使用默认的DefaultCallAdapter对象;
  4. 第四步创建ConverterAdapter,这里我们一般使用Gson来解析响应结果,转换成数据类,这边假设使用的是GsonResponseBodyConverter对象;
  5. 第五步仅仅是获取Retrofit对象中callFactory
  6. 第六步和第七步对Kotlin的挂起函数做判断,分析过程中默认非挂起函数,直接进入第七步,烦恼会一个CallAdapted对象。

到这我们发现Retrofit.create()方法中,动态代理中loadServiceMethod()方法最终返回的就是CallAdapted对象,loadServiceMethod()方法后面直接调用了invoke()方法,接下来进入CallAdapted.invoke()方法。

CallAdapter.invoke()

进入CallAdapted对象中,发现并没有invoke()方法的实现,只能退而求其次去看它的父类了:

retrofit2.HttpServiceMethod#invoke  

@Override
final @Nullable ReturnT invoke(Object[] args) {
  Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
  return adapt(call, args);
}

invoke()方法中首先创建了一个OkHttpCall对象,然后调用adapt()方法。

OkhttpCallRetrofit用来管理发起网络调用和回调的具体类。

CallAdapted.adapt()

retrofit2.HttpServiceMethod.CallAdapted#adapt

@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
  // 这里的callAdapter为DefaultCallAdapterFactory中CallAdapter
  return callAdapter.adapt(call);
}

retrofit2.DefaultCallAdapterFactory#get

return new CallAdapter<Object, Call<?>>() {
  @Override
  public Type responseType() {
    return responseType;
  }

  @Override
  public Call<Object> adapt(Call<Object> call) {
    return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
  }
};

adapt()方法直接调用callAdapter.adapt()方法,前面我们说到,默认使用CallAdapter,它是有DefaultCallAdapterFactory创建而成,CallAdapter.adapt()方法中只是创建了一个ExecutorCallbackCall对象。

到这里我们就清除了,在使用章节第四步生成的Call其实就是ExecutorCallbackCall对象,让他调用它的enqueue()方法就可以发起网络请求。

ExecutorCallbackCall.enqueue()

retrofit2.DefaultCallAdapterFactory.ExecutorCallbackCall#enqueue

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
  this.callbackExecutor = callbackExecutor;
  this.delegate = delegate;
}

public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(callback, "callback == null");
  
  // 直接使用代理Call的enqueue方法,这里的delegate就是OkHttpCall对象
  delegate.enqueue(
      new Callback<T>() {
        @Override
        public void onResponse(Call<T> call, final Response<T> response) {
          // 主线程中回调结果,Android默认callbackExecutor为MainExecutor
          callbackExecutor.execute(
              () -> {
                if (delegate.isCanceled()) {
                  // Emulate OkHttp's behavior of throwing/delivering an IOException on
                  // cancellation.
                  callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                } else {
                  callback.onResponse(ExecutorCallbackCall.this, response);
                }
              });
        }

        @Override
        public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
        }
      });
}

ExecutorCallbackCall.enqueue()方法很简单,直接调用delegate.enqueue(),然后将网络请求的结果回调到主线程中去处理。

这里的delegateCallAdapted.invoke()方法可以得知,它就是OkHttpCall对象,接着我们进入OkhttpCall.enqueue()方法。

OkHttpCall.enqueue()

retrofit2.OkHttpCall#enqueue

public void enqueue(final Callback<T> callback) {
  Objects.requireNonNull(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 {
        call = rawCall = createRawCall(); ②
      } catch (Throwable t) {
        throwIfFatal(t);
        failure = creationFailure = t;
      }
    }
  }

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

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

  call.enqueue( ③
      new okhttp3.Callback() {
        @Override
        public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
          ...
        }

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

此方法才是最终调用Okhttp3发起网络请求最终地点,重要的一共有三步:

  1. 第一步检查rawCall是否为空;
  2. 第二步如果rawCall为空,需要通过createRawCall()来创建OKhttp3.Call对象;
  3. 使用Okhttp3.Call.enqueue()方法来发起异步请求。

到这为止,整个Retrofit网络请求流程就分析结束了,Retrofit并没有执行任何网络请求操作,只是将所有方法注解、参数注解解析好之后,交给Okhttp3来发起具体的网络请求。

最后

为了帮助大家能够更好地学习框架源码,特在此为大家分享一份阿里大佬整理的《Android百大框架源码解析》,这份资料有1880页,干货十足。除了比较流行的Retrofit,OkHttp等,还收纳了很多经典框架。虽然有些框架我们不再使用,但还是可以通过源码,来领略其中的精髓思想,为自己开阔思路。

适用读者范围:

  • 正在入门Android的新手——Android初级开发工程师 初出茅庐
  • Android初级开发工程师——中级开发工程师 知其然知其所以然
  • 中级、高级、资深工程师 知其然知其不可然

alt

需要这份1880页《Android百大框架源码解析》的朋友可以【点击这里直达免费获取方式!!!