从源码处理一理Retrofit的异步网络请求如何把结果切换到主线程

前提,需要具备的知识点是:动态代理,反射,注解。

场景:某日面试的时候被问道,Retrofit异步网络请求是怎么把结果返回给主线程的?

答曰:具体原理不是很清楚,最后应该是通过handler把结果发送到主线程的吧。。。

问:你确定吗?真是handler吗?

这一问把我问懵逼了,心里在想Android世界里把结果在线程间切换最好用的不就是Handler吗。难道还有其他更好的方式。因为自己确实没有阅读Retrofit的源码,平时都是停留在使用层面,这个问题还真是答不上来。为了能揭开心中的谜团,今天就来顺着源码理一理,看看到底是怎么回事。

首先普及一下什么是Retrofit以及使用它的优势:

1 . 什么是Retrofit?

Retrofit是针对于Android/Java的、基于okHttp的、一种轻量级且安全的、并使用注解方式的网络请求框架。

2 . 我们为什么要使用Retrofit,它有哪些优势?

首先,Retrofit使用注解方式,大大简化了我们的URL拼写形式,而且注解含义一目了然,简单易懂;

其次,Retrofit使用简单,结构层次分明,每一步都能清晰的表达出之所以要使用的寓意;

再者,Retrofit支持同步和异步执行,使得请求变得异常简单,只要调用enqueue/execute即可完成;

最后,Retrofit更大自由度的支持我们自定义的业务逻辑,如自定义Converters。

大体使用方式可参考这篇文章:https://blog.csdn.net/guiman/article/details/51480497

1,首先我们需要在AS里面配置相应的依赖包

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

retrofit其实是封装了okhttp,具体的网络请求是okhttp来执行的,而okhttp会依赖okio来实现io处理,这两个框架都包含在了retrofit中,不需要再单独引用。

2,其次新建执行相应网络请求java接口文件,用于存放所有的请求,如:

public interface GitHubService {

}

里面可以添加需要的网络请求的方法,最终所有方法都应该返回Call<T泛型>类型的对象,这个对象后面会用来进行具体的网络请求。

这里为GitHubService这个接口新增了一个listRepos方法,返回一个Call<List<Repo>>对象:

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

该方法上的注解和参数里的注解详细可查看上面的参考链接,都是retrofit进行网络通信的通用做法。可以看到,通过注解的方式,为该方法指定了具体的网络请求方法(即get,post,header等等)以及网络请求的路径。这些网络请求方式和访问路径会在后面的流程中通过动态代理的方式解析到Call<List<Repo>>这个对象里面保存起来,这里声明的listRepos方法其实是为了构建后面返回值对象的,毕竟实际使用的时候我们不会去实现这个接口实现这个方法。

3,接下来,我们要开始真正执行网络请求了,一般会在主线程中这么做:

Retrofit retrofit = new Retrofit.Builder() // 通过建造者方式来构建一个retrofit对象
        .baseUrl("") // 这里是网络请求地址
        .addConverterFactory(GsonConverterFactory.create()) //具体的数据转换器
        .build(); // 生成一个retrofit对象

先是构建了一个retrofit对象,一般情况下,我们网络请求的域名地址相对是固定的,所以在一个网络请求周期内,其实就可以复用这一个retrofit对象,而不同的访问路径就是根据上面定义的api接口中的路径来区别了。(当然想要动态改变域名地址来进行访问的那又另当别论了)

GitHubService gitHubService = retrofit.create(GitHubService.class);

接着,使用retrofit的create方法,创建了一个GitHubService接口对象,到此时,这个接口对象被实例化了。我们上面说了,我们不会主动去实现这个接口,所以这是这个接口对象唯一能被实现的地方。这个create方法里面就是用到了动态代理的方式。了解动态代理的都知道,要实现一个动态代理是这样的:

// 以GitHubService为例创建一个被代理者对象
final GitHubService gitReal = new GitHubService() {
    @Override
    public Call<List<Repo>> listRepos(String user) {
        return null;
    }
};
GitHubService gitProxy = (GitHubService)Proxy.newProxyInstance(GitHubService.class.getClassLoader()
        , new Class<?>[] {GitHubService.class},
        new InvocationHandler() { // 只需要实现InvocationHandler里面的invoke方法
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 所有执行gitReal这个对象的方法都会调用invoke方法
                // 我们可以在这个方法前执行一些自己定制的东西
                method.invoke(gitReal,args); // 甚至可以不执行method,完全自由发挥(retrofit就是这么干的)
                // 也可以在后面执行一些定制的
                return null; // 如果返回的是method.invoke的结果的话,就是返回了Call<List<Repo>>
            }
        });

当我们执行代理者对象gitProxy的的listRepos方法时,其实内部执行的是invoke方法,在invoke方法里面我们可以做所有想做的事情,这就是动态代理的美妙之处。(create的运行机制可以参照这里

那么在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, 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);
          OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          return serviceMethod.callAdapter.adapt(okHttpCall);
        }
      });
}

可以看到create方法最终会返回一个Proxy.newProxyInstance代理出来的对象,被代理者就是我们传进去的泛型,在这里就是GitHubService。我们只关注InvocationHandler的invoke方法,看invoke方法最后一行return返回的是serviceMethod.callAdapter.adapt,看看adapt的返回值 

<R> T adapt(Call<R> call);

是一个泛型,由于我们在GitHubService中的listRepos定义的返回值Call类型的,那么这里可以肯定这个泛型类型就是Call了。也就是说在整个invoke方法中,干了这么多活就会为了构造一个Call对象出来。接下来看看这个Call对象是怎么构造出来的。

抽取最关键的两个地方:

第一,从缓存中获取一个ServiceMethod对象,没有的话就根据当前method和retrofit对象构建一个出来

ServiceMethod serviceMethod = loadServiceMethod(method);
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;
}

看下ServiceMethod.Builder的构造方法:

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方法的注解,类型等等。再看下build方法:

public ServiceMethod build() {
  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 = createResponseConverter();

  for (Annotation annotation : methodAnnotations) {
    parseMethodAnnotation(annotation);
  }

  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }

  if (!hasBody) {
    if (isMultipart) {
      throw methodError(
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    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];
  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.");
    }

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

关键看下第一句:callAdapter = createCallAdapter();

private CallAdapter<?> createCallAdapter() {
  Type returnType = method.getGenericReturnType();
  if (Utils.hasUnresolvableType(returnType)) {
    throw methodError(
        "Method return type must not include a type variable or wildcard: %s", returnType);
  }
  if (returnType == void.class) {
    throw methodError("Service methods cannot return void.");
  }
  Annotation[] annotations = method.getAnnotations();
  try {
    return 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实例中获取CallAapter对象。

public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
  return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
    Annotation[] annotations) {
  checkNotNull(returnType, "returnType == null");
  checkNotNull(annotations, "annotations == null");

  int start = adapterFactories.indexOf(skipPast) + 1;
  for (int i = start, count = adapterFactories.size(); i < count; i++) {
    CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
      return adapter;
    }
  }
。。。。。。省略
}

这里截取了一部分,最主要的就是从adapterFactories这个集合中根据返回值和注解返回具体的CallAdapter对象。再看下这个集合是在哪里赋值的:

private final List<CallAdapter.Factory> adapterFactories;
private final Executor callbackExecutor;
private final boolean validateEagerly;

Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
    List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
    Executor callbackExecutor, boolean validateEagerly) {
  this.callFactory = callFactory;
  this.baseUrl = baseUrl;
  this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
  this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
  this.callbackExecutor = callbackExecutor;
  this.validateEagerly = validateEagerly;
}

查了一圈是在构造retrofit的时候,也就是说在使用Retrofit.Builder进行创建retrofit的时候赋值的。我们先暂时不去看这里。回到刚才的ServiceMethod.Builder的build方法内,

return new ServiceMethod<>(this);

最后一句,创建了一个ServiceMethod对象,该对象存储了解析GitHubServices后的所有信息,还有一个CallAapter对象

ServiceMethod(Builder<T> builder) {
  this.callFactory = builder.retrofit.callFactory();
  this.callAdapter = builder.callAdapter;
  this.baseUrl = builder.retrofit.baseUrl();
  this.responseConverter = builder.responseConverter;
  this.httpMethod = builder.httpMethod;
  this.relativeUrl = builder.relativeUrl;
  this.headers = builder.headers;
  this.contentType = builder.contentType;
  this.hasBody = builder.hasBody;
  this.isFormEncoded = builder.isFormEncoded;
  this.isMultipart = builder.isMultipart;
  this.parameterHandlers = builder.parameterHandlers;
}

最后,回到retrofit的create的InvocationHandler的invoke方法的最后一行:

ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

调用了ServiceMethod的CallAapter对象的adapt方法生成了Call对象,也就是说当我们执行GitHubService的listRepos方法的时候其实是执行了invoke方法返回了一个Call对象。

纵观源码后看到,CallAapter是个接口,里面有一个Factory抽象类:

public interface CallAdapter<T> ,
abstract class Factory,

真正创建Call的必然是其实现类:

DefaultCallAdapterFactory这个类直接返回了传进去的参数:
final class DefaultCallAdapterFactory extends Factory {
    static final Factory INSTANCE = new DefaultCallAdapterFactory();

    DefaultCallAdapterFactory() {
    }

    public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
            return null;
        } else {
            final Type responseType = Utils.getCallResponseType(returnType);
            return new CallAdapter<Call<?>>() {
                public Type responseType() {
                    return responseType;
                }

                public <R> Call<R> adapt(Call<R> call) {
                    return call;
                }
            };
        }
    }
}

如果这里的callAapter是这个的话,很显然return ServiceMethod.callAdapter.adapt(okHttpCall)就直接返回了okHttpCall了。

如果是ExecutorCallAdapterFactory的话,它会返回一个继承自Call的ExecutorCallbackCall对象:

final Executor callbackExecutor;

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

public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
        return null;
    } else {
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Call<?>>() {
            public Type responseType() {
                return responseType;
            }

            public <R> Call<R> adapt(Call<R> call) {
                return new ExecutorCallAdapterFactory.ExecutorCallbackCall(ExecutorCallAdapterFactory.this.callbackExecutor, call);
            }
        };
    }
}
ExecutorCallbackCall的实现是这样的:
static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
        this.callbackExecutor = callbackExecutor; //这个是ExecutorCallAdapterFactory的构造函数中传入的
        this.delegate = delegate; // 其实就是okHttpCall,用来执行真正的网络请求
    }

    public void enqueue(final Callback<T> callback) {
        if (callback == null) {
            throw new NullPointerException("callback == null");
        } else {
            this.delegate.enqueue(new Callback<T>() {
                public void onResponse(Call<T> call, final Response<T> response) {
                    ExecutorCallbackCall.this.callbackExecutor.execute(new Runnable() {
                        public void run() {
                            if (ExecutorCallbackCall.this.delegate.isCanceled()) {
                                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                            } else {
                                callback.onResponse(ExecutorCallbackCall.this, response);
                            }

                        }
                    });
                }

                public void onFailure(Call<T> call, final Throwable t) {
                    ExecutorCallbackCall.this.callbackExecutor.execute(new Runnable() {
                        public void run() {
                            callback.onFailure(ExecutorCallbackCall.this, t);
                        }
                    });
                }
            });
        }
    }

    public boolean isExecuted() {
        return this.delegate.isExecuted();
    }

    public Response<T> execute() throws IOException {
        return this.delegate.execute();
    }

    public void cancel() {
        this.delegate.cancel();
    }

    public boolean isCanceled() {
        return this.delegate.isCanceled();
    }

    public Call<T> clone() {
        return new ExecutorCallAdapterFactory.ExecutorCallbackCall(this.callbackExecutor, this.delegate.clone());
    }

    public Request request() {
        return this.delegate.request();
    }
}
它有两个成员变量,一个是Executor类型的用来安排执行任务,从Factory的构造函数而来,另一个是Call类型,用来执行真正网络请求的由CallAapter的adapt()方法的参数赋值。

 至此,我们分析了GitHubService gitHubService = retrofit.create(GitHubService.class); retrofit中create执行的逻辑:本质上就是生成一个代理类,里面的InvocationHandler的invoke方法为我们返回一个Call对象。但是我们程序执行到这里的时候,还不会去回调invoke方法,只有当我们执行了这个:

Call<List<Repo>> call = gitHubService.listRepos("user");

才会去执行invoke方法,为我们生成了具有Call接口类型的具体对象,那么这个具体对象是谁呢?

是DefaultCallAdapterFactory中直接返回的okHttpCall,还是ExecutorCallAdapterFactory的ExecutorCallbackCall呢?

别急,我们最后再来看看使用retrofit的最后一步:网络请求。上面的几个步骤都是为最后一步来服务的。

// 执行异步
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) {

    }
});
// 或执行同步
try {
    Response<List<Repo>> response = call.execute();
} catch (IOException e) {
    e.printStackTrace();
}

有两种方式,同步和异步。所谓同步就是需要等待网络请求的结果,很显然如果我们在主线程调用的话execute的执行时间过长就会导致主线程被阻塞从而产生ANR,所以一般我们会去使用异步的方式,不管网络请求有多慢多长时间,只要有了结果就会回调Callback的相应的方法,从而可以直接用结果来更新UI。所以enqueue必然含有工作线程和UI线程的切换。这也正是本文开头提到的,retrofit是使用什么方式来切换线程的,是使用handler吗?

看看实现了Call接口的enqueue都有哪些具体类:

这里就不分析OkHttpCall了,这个就是真正执行网络请求的类,它的异步和同步的执行是走的okHttp框架里的Call接口的,暂时不去关心。那么就只剩下ExecutorCallbackCall了,这个是不是很熟悉呢,在分析动态代理的invoke方法生成Call对象的时候分析过。所以我们回到这个类里面看看它的enqueue是如何实现的。

 

@Override public void enqueue(final Callback<T> callback) {
  if (callback == null) throw new NullPointerException("callback == null");

  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()) {
            // 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(new Runnable() {
        @Override public void run() {
          callback.onFailure(ExecutorCallbackCall.this, t);
        }
      });
    }
  });
}

直接使用delegate的enqueue方法,前面分析过这个delegate实际就是OkHttpCall对象,里面走的就是okhttp的流程。最终有结果后会执行Callback的相应的回调方法,在这两个方法里面都用到了callbackExecutor这个任务对象来安排执行一个runnable对象,在runnable里面又回调了用户传入的Callback接口。那么callbackExecutor到底做了什么呢?

回顾上面,这个callbackExecutor是在ExecutorCallAapterFactory的构造函数里面传入的

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

这个构造函数在Platform这个类里面有三处调用:

因为我们是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);
    }
  }
}

有两个方法,一个生成Executor,一个生成Factory,我们看生成的Executor对象其内部是创建了一个关联到主线程的handler,执行execute方法的时候,直接把runnable对象传递给handler加入到消息队列中。这里正是把网络请求的结果发送到UI线程中的执行逻辑,所以说在Android平台下,retrofit还是使用了handler来负责工作线程到主线程的切换工作的。

我觉得,之所以有面试官会问确定是使用handler来实现的么?其实大概考虑retrofit这个框架不仅仅是给Android用的,它还支持java平台和ios平台的实现,这两个平台有自己的切换逻辑,就比如java平台来说的话,其内部是通过okhttp框架的异步同步方法进行的,这里暂时不去跟踪了,以后遇到再去详细看下。

到这里,我们还只是知道了Android平台下executor的生成,那么它们是在哪里被调用的呢?

只有一处地方,就是在retrofit被构建的时候

public Retrofit build() {
  if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
  }

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

  Executor callbackExecutor = this.callbackExecutor;
  if (callbackExecutor == null) {
    callbackExecutor = platform.defaultCallbackExecutor(); // 先创建Executor
  }

  // Make a defensive copy of the adapters and add the default Call adapter.
  List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
  adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));//再将期赋值给Factory,然后再保存到集合中,这个结合在执行动态代理invoke的时候,创建ServiceMethod的时候会被查询到

  // Make a defensive copy of the converters.
  List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

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

也就是在最开始的时候就已经创建好了各种资源。

retrofit提供给用户的接口非常简单便捷,但是其内部实现却很复杂甚至有点绕,不细心扣每一处细节的话,还真不一定能搞明白。

 

到此------完------

 

参考文章:

Retrofit之OkhttpCall执行原理详解

Retrofit 原理 运行机制详解

Android 网络框架之Retrofit2使用详解及从源码中解析原理

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值