Retrofit实现原理浅析

Retrofit是一个基于AOP思想,对RestfulApi注解进行动态代理的网络框架;

一.基本用法

1.定义接口

public interface INetApiService { 
    @GET("/demobiz/api.php") 
    Call<BizEntity> getBizInfo(@Query("id") String id); 
} 

在这个接口定义中,用注解@GET("/demobiz/api.php")声明了url路径,用注解@Query("id") 声明了请求参数;

最重要的是,用Call声明了返回值是一个Retrofit的Call对象,并且声明了这个对象处理的数据类型为BizEntity,BizEntity是我们自定义的数据模型;

2.依次获得Retrofit对象、接口实例对象、网络工作对象

//新建一个Retrofit对象 
Retrofit retrofit=new Retrofit.Builder() 
.baseUrl(Config.DOMAIN)//要访问的网络地址域名,如http://www.zhihu.com 
.addConverterFactory(GsonConverterFactory.create()) 
.build(); 
... 
//用retrofit加工出对应的接口实例对象 
INetApiService netApiService= retrofit.create(INetApiService.class); 
//可以继续加工出其他接口实例对象 
IOtherService otherService= retrofit.create(IOtherService.class); 
··· 
//调用接口函数,获得网络工作对象 
Call<BizEntity> callWorker= netApiService.getBizInfo("id001"); 

这个复杂的过程下来,最终得到的callWorker对象,才可以执行网络访问。

3.访问网络

用上一步获取的worker对象,执行网络请求

callWorker.enqueue(new Callback<BizEntity>() { 
            @Override 
            public void onResponse(Call<BizEntity> call, Response<BizEntity> response) {...} 
            @Override 
            public void onFailure(Call<BizEntity> call, Throwable t) {...} 
        }); 

在回调函数里,取得我们需要的BizEntity数据对象

二、原理浅析

61c9e61f0ba94f05f3ed1c02b4ced800.jpg

1.retrofit.create(interface)

Retrofit对象的构建就是简单的builder模式,直接看create

//Retrofit.java 
public <T> T create(final Class<T> service) { 
    //验证 
    validateServiceInterface(service); 
    return (T) 
        //动态代理 
        Proxy.newProxyInstance( 
        service.getClassLoader(), //类加载器 
        new Class<?>[] {service}, //一组接口 
        new InvocationHandler() { 
            //判断android和jvm平台及其版本 
            private final Platform platform = Platform.get(); 
            @Override 
            public Object invoke(Object proxy, Method method, Object[] args){ 
                //如果该方法是Object的方法,直接执行不用管 
                if (method.getDeclaringClass() == Object.class) { 
                    return method.invoke(this, args); 
                } 
                //isDefaultMethod:检查是否是java8开始支持的接口默认方法 
                return platform.isDefaultMethod(method) 
                    ? platform.invokeDefaultMethod(method, service, proxy, args) 
                    : loadServiceMethod(method).invoke(args); //我们关注这里 
            } 
        }); 
} 

Proxy.newProxyInstance动态代理,运行期会生成一个类(字节码)如$ProxyN,实现传入的接口即WanApi,重写接口方法然后转发给InvocationHandler的invoke

class $ProxyN extends Proxy implements WanApi{ 
    Call<WanArticleBean> articleList(@Path("page") int page){ 
        //转发给invocationHandler 
        invocationHandler.invoke(this,method,args); 
    } 
} 
1.1.validateServiceInterface验证逻辑
//Retrofit.java 
private void validateServiceInterface(Class<?> service) { 
    //检查:WanApi不是接口就抛异常... 
    //检查:WanApi不能有泛型参数,不能实现其他接口... 
    if (validateEagerly) { //是否进行严格检查,默认关闭 
        Platform platform = Platform.get(); 
        for (Method method : service.getDeclaredMethods()) { //遍历WanApi方法 
            //不是默认方法,并且不是静态方法 
            if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) { 
                //把方法提前加载进来(检查下有没有问题) 
                loadServiceMethod(method); 
            } 
        } 
    } 
} 

如果开了validateEagerly,会一次性把接口WanApi的所有方法都检查一遍并加载进来,可以在debug模式下开启,提前发现错误写法,比如在@GET请求设置了@Body这种错误就会抛出异常:

java.lang.IllegalArgumentException: Non-body HTTP method cannot contain @Body.

1.2 loadServiceMethod

loadServiceMethod(Method method)方法的作用是解析我们传进来的服务中的方法,这里是WanApi中的方法,并将解析之后的结果包装成一个ServiceMethod对象,放进一个缓存中。即缓存服务方法对应的请求信息,这样下次我们就不需要再次解析了。

//Retrofit.java 
//缓存,用了线程安全ConcurrentHashMap 
final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>(); 
ServiceMethod<?> loadServiceMethod(Method method) { 
    ServiceMethod<?> result = serviceMethodCache.get(method); 
    //WanApi的articleList方法已缓存,直接返回 
    if (result != null) return result; 
    synchronized (serviceMethodCache) { 
        result = serviceMethodCache.get(method); 
        if (result == null) { 
            //解析articleList的注解,创建ServiceMethod并缓存起来 
           result = new ServiceMethod.Builder<>(this, method).build();
            serviceMethodCache.put(method, result); 
        } 
    } 
    return result; 
} 
#ServiceMethod.java
public ServiceMethod build() {
     // 创建合适的适配器  
     callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      responseConverter = createResponseConverter();
 //解析方法注解如GET 
        for (Annotation annotation : methodAnnotations) { 
            parseMethodAnnotation(annotation); 
        } 
        //省略各种检查... 
        //解析参数注解如Path 
        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); 
        } 
        //省略各种检查... 
      return new ServiceMethod<>(this);
    }

1.3 createCallAdapter

//ServiceMethod.java
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
    Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
  try {
    //noinspection unchecked
    //调用retrofit的callAdapter方法
    return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
  } catch (RuntimeException e) { // Wide exception range because factories are user code.
    throw methodError(method, e, "Unable to create call adapter for %s", returnType);
  }
}

//Retrofit.java
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
...
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
  //调用nextCallAdapter
  return nextCallAdapter(null, returnType, annotations);
}

public CallAdapter<?, ?> nextCallAdapter(
    @Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {

  ...

  //遍历 callAdapterFactories
  int start = callAdapterFactories.indexOf(skipPast) + 1;
  for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
    //是具体CallAdapterFactory的 get 方法
    CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
      return adapter;
    }
  }
  ...
}

CallAdapter是根据returnType和annotations的类型,从callAdapterFactories工厂中进行查找,从而返回所对应的网络请求适配器,这里returnType指的是网络请求接口里方法的返回值类型,如Call、Observable等。annotations则指代的是注解类型,如@GET、@POST等。

callAdapterFactories中存放的是CallAdapter.Factory,调用factory.get(returnType,annotations,retrofit)来获取到CallAdapter;Factory是个抽象类,get()是其抽象方法,那么它的实现类是谁?

1.4 ExecutorCallAdapterFactory

回到Retrofit.Builder

//Retrofit.Builder.java 
public Retrofit build() { 
    Executor callbackExecutor = this.callbackExecutor; 
    //如果没设置线程池,则给android平台设置一个默认的MainThreadExecutor(用Handler将回调切回主线程) 
    if (callbackExecutor == null) { 
        callbackExecutor = platform.defaultCallbackExecutor(); 
    } 
    List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories); 
    //添加平台默认的CallAdapterFactory 
    callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor)); 
} 

platform.defaultCallAdapterFactories 指的是Android平台的一个默认的适配器工厂,当我们不使用自定义适配器工厂时,则添加的就是这默认的工厂。这里提到了自定义适配器工厂,其实我们在使用Retrofit的时候,有时候会和RxJava结合,例如在创建Retrofit时,也会addCallAdapterFactory,将RxJava2CallAdapterFactory添加到callAdapterFactories中。

添加调用适配器工厂的目的就是支持Call以外的服务方法返回类型,如支持Observable,Single返回类型等。

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

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      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);
      }
    }
  }

ExecutorCallAdapterFactory这个工厂创建具体的CallAdapter实例。在callAdapterFactories集合器添加一个默认适配器工厂时,也附带传进去了一个参数callbackExecutor,callbackExecutor是Java8或者Android平台的一个默认线程调度器,它的作用涉及到一个线程切换的问题。

//ExecutorCallAdapterFactory.java 
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) { 
   if (getRawType(returnType) != Call.class) {
      return null;
    }

    final Type responseType = Utils.getCallResponseType(returnType);
   return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
} 

回到Retrofit.create()方法中:

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      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, @Nullable 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<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

T adapt(Call<R> call) {
    return callAdapter.adapt(call);
  }
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;

    CallAdapted(
        RequestFactory requestFactory,
        okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

    @Override
    protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }
  }

创建好了ServiceMethod,也创建好了CallAdapter,接着就创建了OkHttpCall对象,然后调用了callAdapter的适配方法adapter(call)。

OKHttpCall

OkHttpCall其实是对OkHttp中的realCall进行了一层包装, 在Retrofit里,OkHttpCall紧密连接OkHttp,它的内部同样可以调用同步execute、 异步execute方法进行网络请求,其实真正调用的也就是OkHttp的execute和execute方法。在此同时,Retrofit中会对请求响应也做了解析。

可以看到callAdapter.adapt(call)会使⽤⼀个 CallAdapter 对象来把 OkHttpCall 对象进⾏转换,⽣成⼀个新的对象,默认情况下,该方法返回的是⼀个 ExecutorCallbackCall对象 ,它的主要作⽤是把操作切回主线程后再交给 Callback 。

//ExecutorCallbackCall.java 
void enqueue(final Callback<T> callback) { 
    delegate.enqueue( 
        new Callback<T>() { 
            @Override 
            public void onResponse(Call<T> call, final Response<T> response) { 
                //将回调切回主线程 
                callbackExecutor.execute( 
                    () -> { 
                        callback.onResponse(ExecutorCallbackCall.this, response); 
                    }); 
                //... 
            } 
            @Override 
            public void onFailure(Call<T> call, final Throwable t) {} 
        }); 
} 
小结一下

retrofit.create()的主要作用就在于将网络请求方法中的信息进行初步的处理,解析api service具体的接口方法的注解、参数等,解析接口后又生成了一个ServiceMethod对象,并且利用这个ServiceMethod对象创建了一个CallAdapter和OkHttpCall。

2.请求入队->call.enqueue(callback)

准备好了OKHttpCall对象,接下来就是调用OKHttpCall的同步方法execute或者异步方法enqueue请求入队,以enqueue方法为例:

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

    okhttp3.Call call;
    synchronized (this) {
      ...
      if (call == null && failure == null) {
        try {
        // focus1-创建一个okhttp3.Call 对象,也即创建一个HTTP请求
          call = rawCall = createRawCall();
        }
      }
    ...
    call.enqueue(
        new okhttp3.Callback() {
          @Override
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
            Response<T> response;
            try {
              //解析请求返回值
              response = parseResponse(rawResponse);
            }
              ...
            try {
              callback.onResponse(OkHttpCall.this, response);
            }

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

          private void callFailure(Throwable e) {
            try {
              callback.onFailure(OkHttpCall.this, e);
            }
          }
        });
  }

注意focus1处的代码创建了一个okhttp3.Call对象

okhttp3.Call是如何创建的?

先来看看createRawCall方法的代码:

# OKHttpCall.java
private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

#ServiceMethod.java
okhttp3.Call toCall(@Nullable Object... args) throws IOException {
    RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
        contentType, hasBody, isFormEncoded, isMultipart);

   // ...
    return callFactory.newCall(requestBuilder.build());
  }

可以看到okhttp3.Call最终是 通过callFactory创建,我们知道OkHttp3的call的创建如下代码所示: okHttpClient.newCall(request);request参数我们通过RequestBuilder来构建,那么callFactory对象是从何而来的呢?
okhttp3.Call.Factory来自于Retrofit,在Retrofit中的构建者模式中可以找到:

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

真相大白了!原来Retrofit用来创建OkHttp3的Call的工厂就是OkHttp3的OkHttpClient:callFactory = new OkHttpClient();

回到OKHttpCall的enqueue方法,总结一下它的流程:

1.内部首先调用了一个createRawCall()方法,创建rawCall,这个rawCall其实指代的就是Okhttp3的call,也就是OkHttp进行网络请求调用的一个调度器;

2.创建好OkHttp的call后,就开始调用enqueue进行异步请求,发现在异步请求内响应的回调属于okhttp3.Callback,所返回来的结果,也都是okhttp3.Response,到这里就可以大概知道了,retrofit的网络请求其实还是由OkHttp来实现。

3.okhttp3.Response这个响应不方便开发者直接使用,所以retrofit在收到结果后,又对响应结果进行新一轮的解析 response = parseResponse(rawResponse),以Response对象的形式返回给开发者。

3.如何解析返回结果

查看OkHttpCall的parseResponse方法:

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

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      
      T body = responseConverter.convert(catchingBody);
      return Response.success(body, rawResponse);
      
    } catch (RuntimeException e) {
      
    }
  }

可以看到,网络请求的结果最终会被一个返回结果转换器进行转换之后再返回,不难猜测,这个responseConverter转换器的功能,就是将网络返回的不易看懂的数据转换为我们需要的自定义的Object接收对象,如UserBean、XXListBean等。现在我们已经有了原始的返回对象,接下只需要知道这个responseConverter对象是什么以及它的convert()方法即可。

responseConverter

responseConverter是在ServiceMethod的build中创建的:

# ServiceMethod.java
 private Converter<ResponseBody, T> createResponseConverter() {
      Annotation[] annotations = method.getAnnotations();
      try {
        return retrofit.responseBodyConverter(responseType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create converter for %s", responseType);
      }
    }

很容易又继续追到retrofit.responseBodyConverter(responseType, annotations)方法:

 public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
    return nextResponseBodyConverter(null, type, annotations);
  }

继续追到Retrofit里的nextResponseBodyConverter(null, type, annotations);:

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

  ...
  }

converterFactories.get(i).responseBodyConverter(type, annotations, this);可以看到,这个返回结果转换器responseConverter来自于converterFactories,在Retrofit中寻找converterFactories,在Retrofit的建造者Builder的build()方法中我们找到:

public Retrofit build() {
      ...
      // ensures correct behavior when using converters that consume all types.
List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      ...
    }

第一行被add的converter不是我们自定义的,暂不去看,去寻找第二行addAll(this.converterFactories)中的converterFactories,看它是哪里被赋值构建的,我们于是又在Retrofit中找到了addConverterFactory()方法:

 public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
      return this;
    }

这个方法是不是很眼熟,回忆一下我们平时是怎么使用Retrofit的:

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

这不就是我们在一开始在创建Retrofit时常用的添加Gson转换器的地方嘛!原来这个Gson转化器在Retrofit里是最终被用到OkHttpCall的parseResponse()中的responseConverter.convert(catchingBody);啊!原来Retrofit内部的网络结果转换器是这样工作的!

线程切换原理

相比OkHttp3,Retrofit在使用时一个很明显的方便之处就是在 Call.execute() 或者 Call.enqueue() 来发起请求后的返回结果事件中,不需要再切换线程,因为此刻,它已经在安卓的UI主线程当中了。

在执行retrofit2.Call的enqueue()方法时,我们需要注意,这个Call对象我们刚刚是在Retrofit中的create()方法创建的OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
但是这个OkHttpCall会经过adapt转为真正进行请求的另一个类ExecutorCallbackCall,它里面会持有OkHttpCall并在执行enqueue方法时去执行OkHttpCall的enqueue方法。

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }
    // 执行callAdapter的adapt方法时,将Call包装为了ExecutorCallbackCall对象
      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

   // ...
    @Override public void enqueue(final Callback<T> callback) {
      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                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(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }
}

前面已经提到过,这个ExecutorCallbackCall的作用就是为了线程切换,具体的工作是交给了callbackExecutor这个线程调度器来做的,而默认callbackExecutor的创建在Retrofit的初始化中,callbackExecutor = platform.defaultCallbackExecutor();

static final class Android extends Platform {

    @Override
    public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

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

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

platform是一个Android平台,defaultCallbackExecutor 内部其实调用的是 new MainThreadExecutor() ,很清楚的看到, handler.post(r) 内部使用Handler将响应抛到了主线程。

小结一下

由上面的分析可知,ExecutorCallbackCall中的delegate就是 adapt()传过去的OkHttpCall,callbackExecutor就是我们刚刚看到的MainThreadExecutor,ExecutorCallbackCall在处理返回结果时,使用的是MainThreadExecutor,而MainThreadExecutor又将这个线程切换到了UI主线程中,至此,完成了网络请求过程子线程个UI主线程的切换。

延伸一下:

CallAdapterFactory的作用?

扩展的是对网络工作对象callWorker的自动转换,把Retrofit中执行网络请求的Call对象,转换为接口中定义的Call对象;

Retrofit本身用一个OkHttpCall的类负责处理网络请求,而我们在接口中定义需要定义很多种Call,接口里的Call和Retrofit里的OkHttpCall并不一致,所以我们需要用一个CallAdapter去做一个适配转换;

ConverterFactory的作用?

扩展的是对返回的数据类型的自动转换,把一种数据对象转换为另一种数据对象;

参考:
Android源码进阶之Retrofit
从问题到解析,读懂Retrofit2原理
Retrofit完整剖析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,884评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,212评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,351评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,412评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,438评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,127评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,714评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,636评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,173评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,264评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,402评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,073评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,763评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,253评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,382评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,749评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,403评论 2 358

推荐阅读更多精彩内容