zoukankan      html  css  js  c++  java
  • 04、Android进阶Retrofit原理解析

    Retrofit原理

    Retrofit大致原理如下图所示:

    Retrofit的创建过程

    当我们使用Retrofit请求网络时,首先要写请求接口:

    public interface ApiService {
        @GET("getInfo.php?ip=59.105.23.12")
        Call<IpModel> getIpMsg();
    }
    

    接着,我们通过调用如下代码来创建Retrofit:

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(url)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    

    Retrofit是通过建造者模式构建出来的。接下来查看Builder方法:

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

    查看Platform的get方法,如下所示:

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

    Platform的get方法最终调用的是findPlatform方法,根据不同的运行平台来提供不同的线程池。

    接下来查看build方法,代码如下所示

    public Retrofit build() {
        // baseUrl 是必须指定的
        if (baseUrl == null) {
            throw new IllegalStateException("Base URL required.");
        }
    	// callFactory 默认为this.callFactory
        okhttp3.Call.Factory callFactory = this.callFactory;
        if (callFactory == null) {
            // 如果没有设置callFactory,则直接创建 OkHttpClient
            callFactory = new OkHttpClient();
        }
    
        Executor callbackExecutor = this.callbackExecutor;
        if (callbackExecutor == null) {
            // 重点标记
            callbackExecutor = platform.defaultCallbackExecutor();
        }
    
        // adapterFactories主要用于存储对Call进行转化的对象
        List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
        adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    
        // 的converterFactories主要用于存储转化数据对象
        List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
    
        return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
                callbackExecutor, validateEagerly);
    }
    

    this.callFactory就是我们在构建Retrofit时调用callFactory方法所传进来的,如下所示:

    public Retrofit.Builder callFactory(okhttp3.Call.Factory factory) {
        this.callFactory = checkNotNull(factory, "factory == null");
        return this;
    }
    

    因此,如果需要对 OkHttpClient 进行设置,则可以构建 OkHttpClient 对象,然后调用callFactory方法将

    设置好的OkHttpClient传进去。

    Call的创建过程

    下面我们创建Retrofit实例并调用如下代码来生成接口的动态代理对象:

    ApiService apiService = retrofit.create(ApiService.class);
    

    接下来看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 {
                        // method就是我们定义的getIpMsg方法。
                        if (method.getDeclaringClass() == Object.class) {
                            return method.invoke(this, args);
                        }
                        if (platform.isDefaultMethod(method)) {
                            return platform.invokeDefaultMethod(method, service, proxy, args);
                        }
                        // 创建OkHttpCall
                        ServiceMethod serviceMethod = loadServiceMethod(method);
                        OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                        // 创建 ExecutorCallbackCall,并传入OkHttpCall
                        return serviceMethod.callAdapter.adapt(okHttpCall);
                    }
                });
    }
    

    可以看到 create 方法返回了一个 Proxy.newProxyInstance 动态代理对象。

    当我们调用IpService的 getIpMsg方法时,最终会调用InvocationHandler的invoke方法。它有三个参数:第一个是代理对象,第二个是调用的方法,第三个是方法的参数。

    下面查看loadServiceMethod方法:

    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 查询传入的方法是否有缓存。如果有,就用缓存的ServiceMethod;

    如果没有,就创建一个,并加入 serviceMethodCache 缓存起来。

    下面看ServiceMethod是如何构建的,代码如下所示:

    public ServiceMethod build() {
        // 最终会得到我们在构建Retrofit调用build方法时 adapterFactories添加的对象的get方法
        callAdapter = createCallAdapter();
        // CallAdapter的responseType得到的是返回数据的真实类型
        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?");
        }
        // 遍历converterFactories列表中存储的Converter.Factory,并返回合适的Converter用来转换对象。
        responseConverter = createResponseConverter();
    
        // 遍历parseMethodAnnotation方法来对请求方式(比如GET、POST)和请求地址进行解析。
        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];
            if (Utils.hasUnresolvableType(parameterType)) {
                throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
                        parameterType);
            }
    	    // 对方法中的参数注解进行解析(比如@Query、@Part)。
            Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
            if (parameterAnnotations == null) {
                throw parameterError(p, "No Retrofit annotation found.");
            }
    
            parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
        }
    	......
        // 创建ServiceMethod类并返回。    
        return new ServiceMethod<>(this);
    }
    

    在前面Retrofit的build方法中,adapterFactories 列表默认会添加 defaultCallAdapterFactory。

    CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
        if (callbackExecutor != null) {
            return new ExecutorCallAdapterFactory(callbackExecutor);
        }
        return DefaultCallAdapterFactory.INSTANCE;
    }
    

    defaultCallAdapterFactory指的是ExecutorCallAdapterFactory。 ExecutorCallAdapterFactory的get方法如下所示:

    @Override
    public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
            return null;
        }
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Call<?>>() {
            // 返回真实数据类型ipModel
            @Override public Type responseType() {
                return responseType;
            }
    		// adapt 方法会创建ExecutorCallbackCall,它会将call 的回调转发至UI线程。
            @Override public <R> Call<R> adapt(Call<R> call) {
                return new ExecutorCallAdapterFactory.ExecutorCallbackCall<>(callbackExecutor, call);
            }
        };
    }
    

    get方法会得到CallAdapter对象,CallAdapter的responseType方法会返回数据的真实类型。

    查看Retrofit的create方法,在调用了loadServiceMethod方法后会创建OkHttpCall,OkHttpCall 的构造方法只是进行了赋值操作。紧接着调用 serviceMethod.callAdapter.adapt(okHttpCall)。

    其中,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;
            this.delegate = delegate;
        }
    
        @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()) {
     callback.onFailure(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, new IOException("Canceled"));
                            } else {
                                callback.onResponse(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, response);
                            }
                        }
                    });
                }
    
                @Override public void onFailure(Call<T> call, final Throwable t) {
                    callbackExecutor.execute(new Runnable() {
                        @Override public void run() {
                            callback.onFailure(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, t);
                        }
                    });
                }
            });
        }
    	......
    }
    

    可以看出ExecutorCallbackCall是对Call的封装,它主要添加了通过callbackExecutor将请求回调到 UI 线

    程。当我们得到 Call 对象后会调用它的 enqueue 方法,其实调用的是ExecutorCallbackCall的enqueue方法。

    而从上面代码注释处可以看出ExecutorCallbackCall的enqueue方法最终调用的是delegate的enqueue方法。

    delegate是传入的OkHttpCall。

    Call的enqueue方法

    下面我们就来查看OkHttpCall的enqueue方法,代码如下所示:

    @Override public void enqueue(final Callback<T> callback) {
        if (callback == null) throw new NullPointerException("callback == null");
    
        okhttp3.Call call;
        Throwable failure;
    	......
        // 调用了okhttp3.Call类型的call的enqueue方法    
        call.enqueue(new okhttp3.Callback() {
            @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
                    throws IOException {
                Response<T> response;
                try {
                    // 重要标记
                    response = parseResponse(rawResponse);
                } catch (Throwable e) {
                    callFailure(e);
                    return;
                }
                callSuccess(response);
            }
    
    	......
        });
    }
    

    我们再来看看parseResponse方法:

    Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();
    	......
        int code = rawResponse.code();
        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) {
            return Response.success(null, rawResponse);
        }
    
        OkHttpCall.ExceptionCatchingRequestBody catchingBody = new OkHttpCall.ExceptionCatchingRequestBody(rawBody);
        try {
            // 重要标记
            T body = serviceMethod.toResponse(catchingBody);
            return Response.success(body, rawResponse);
        } catch (RuntimeException e) {
            catchingBody.throwIfCaught();
            throw e;
        }
    }
    

    根据返回的不同状态码code值来做不同的操作。接下来 看toResponse方法:

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

    这个responseConverter就是此前讲过在ServiceMethod的build方法调用createResponseConverter方法返回

    的 Converter。

    在此前的例子中我们传入的是 GsonConverterFactory,因此可以查看GsonConverterFactory的

    代码,如下所示:

    public final class GsonConverterFactory extends Converter.Factory {
      ......  
      @Override
      public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
          Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonResponseBodyConverter<>(gson, adapter);
      }
        ......
    }
    

    在GsonConverterFactory中有一个方法responseBodyConverter,它最终会创建GsonResponse- BodyConverter:

    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 {
        JsonReader jsonReader = gson.newJsonReader(value.charStream());
        try {
          return adapter.read(jsonReader);
        } finally {
          value.close();
        }
      }
    }
    

    在GsonResponseBodyConverter的convert方法里会将回调的数据转换为JSON格式。
    此前调用responseConverter.convert是为了转换为特定的数据格式。Call的enqueue方法主要做的就是用OkHttp来请求网络,将返回的Response进行数据转换并回调给UI线程。

  • 相关阅读:
    mysql 注意事项 PreparedStatement 对比 statement
    Dbutils commons-dbutils-1.3
    C3P0 mysql 5.7
    servlet-应用mysql-1
    javabean 用integer 而不是int
    servlet-1
    servlet 路径 编码 问题
    mac tomcat 9.0
    case end 的用法
    自定义抛出异常
  • 原文地址:https://www.cnblogs.com/pengjingya/p/14948217.html
Copyright © 2011-2022 走看看