Home Retrofit使用和解析
Post
Cancel

Retrofit使用和解析

使用

众所周知,Retrofit只是对OkHttp的一层封装,目的是为了让OkHttp使用起来更便捷。主要就是对Request对象的构建进行了一层封装,使我们只需要定义好借口的形式就OK,使用起来更偏向方法的直接调用(结合协程)。然后是对Call进行了包装对返回结果进行了处理,加入了解析器,可以直接返回我们所需要的对象。

依赖

1
2
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.retrofit2:retrofit:2.9.0")

这里引入的Retrofit中自动包括了OkHttp,所以不需要我们再单独引入了。另外引入了一个gson的json解析器,是常用来解析json的。

创建Retrofit对象

1
2
3
4
5
val retrofit = Retrofit.Builder()
    .baseUrl("https://coding.jiangker.cn")
    .client(OkHttpClient())
    .addConverterFactory(GsonConverterFactory.create())
    .build()

Retrofit和之前的OkHttp类似的,都是用来做统一管理的,这里也可以自己定义OkHttpClient,不过一般都是保持默认,默认也是使用的OkHttpClient所以可以省略。

构建请求接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface RetrofitService {
    @GET("demo")
    fun getUser(
        @Query("name") name: String,
        @Query("age") age: Int
    ): retrofit2.Call<User>

    @GET("demo")
    suspend fun syncGetUser(
        @Query("name") name: String,
        @Query("age") age: Int
    ): User
}

val service = retrofit.create(RetrofitService::class.java)

@Query注解可以相当于往后拼接params参数。常有的还有@Path、@Header、@Body等。对于常规方式是直接返回retrofit2.Call这个对象,较Okhttp的Call对象有更多的能力,但实际发起请求还是依赖OkHttp的call。对于使用kotlin协程的来说,可以直接添加suspend然后直接返回需要的结果。 然后直接使用Retrofit来创建相关接口的服务器对象。

发起请求

call请求

1
2
3
4
5
6
7
8
9
10
11
12
13
userCall.execute()
userCall.enqueue(object :retrofit2.Callback<User>{
    override fun onResponse(
        call: retrofit2.Call<User>,
        response: retrofit2.Response<User>
    ) {
        // 默认主线程返回
    }

    override fun onFailure(call: retrofit2.Call<User>, t: Throwable) {
        // 默认主线程返回
    }
})

可以看出基本上用法和OkHttp一致,这里的切换回主线程经过了一次调度器。可以使用@SkipCallbackExecutor注解去跳过

协程请求

1
2
3
val user: User = withContext(Dispatchers.IO) {
    service.syncGetUser("jiangker", 18)
}

协程请求看着形式上会比普通的call请求更简单,这里请求实际是使用的execute()发起的,所以需要手动去切换子线程。这里对错误结果也没有进行处理的,一般需要进行catch处理。

原理

Retrofit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public final class Retrofit {
    // 接口方法缓存
    private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();

    final okhttp3.Call.Factory callFactory;
    final HttpUrl baseUrl;
    final List<Converter.Factory> converterFactories;
    final List<CallAdapter.Factory> callAdapterFactories;
    final @Nullable Executor callbackExecutor;
    final boolean validateEagerly;

    public <T> T create(final Class<T> service) {
    // 先验证
    validateServiceInterface(service);
    // 动态代理
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];

              @Override
              public @Nullable 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);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
    }
    // 检验传入接口的有效性
    private void validateServiceInterface(Class<?> service) {
        if (!service.isInterface()) {
            throw new IllegalArgumentException("API declarations must be interfaces.");
        }

        Deque<Class<?>> check = new ArrayDeque<>(1);
        check.add(service);
        while (!check.isEmpty()) {
            Class<?> candidate = check.removeFirst();
            if (candidate.getTypeParameters().length != 0) {
                StringBuilder message =
                    new StringBuilder("Type parameters are unsupported on ").append(candidate.getName());
                if (candidate != service) {
                    message.append(" which is an interface of ").append(service.getName());
                }
                throw new IllegalArgumentException(message.toString());
            }
            Collections.addAll(check, candidate.getInterfaces());
        }

        // 是否直接解析所有接口,默认是false的
        if (validateEagerly) {
            Platform platform = Platform.get();
            for (Method method : service.getDeclaredMethods()) {
                if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
                loadServiceMethod(method);
                }
            }
        }
    }

    // 方法解析
    ServiceMethod<?> loadServiceMethod(Method method) {
        // 查找是否已经解析过了
        ServiceMethod<?> result = serviceMethodCache.get(method);
        if (result != null) return result;

        synchronized (serviceMethodCache) {
            result = serviceMethodCache.get(method);
            if (result == null) {
                result = ServiceMethod.parseAnnotations(this, method);
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }
}

Retrofit中有比较多的基础配置,create方法对传入的方法进行动态代理,返回一个接口的代理对象,对方法的解析可以设置为饿汉的形式直接解析全部,也可以是用到一个解析一个。解析是用到了ServiceMethod去进行具体的解析,解析的结果也会缓存到Retrofit的map中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//serviceMethodCache.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // 对注解和参数进行解析
    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);
    }
    if (returnType == void.class) {
        throw methodError(method, "Service methods cannot return void.");
    }

    // 生成请求CallAdapter等
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

RequestFactory中保存着解析后的所有结果,在build方法中进行真正的解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
final class RequestFactory {
    static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
        return new Builder(retrofit, method).build();
    }

    private final Method method;
    private final HttpUrl baseUrl;
    final String httpMethod;
    private final @Nullable String relativeUrl;
    private final @Nullable Headers headers;
    private final @Nullable MediaType contentType;
    private final boolean hasBody;
    private final boolean isFormEncoded;
    private final boolean isMultipart;
    private final ParameterHandler<?>[] parameterHandlers;
    // 是否是协程
    final boolean isKotlinSuspendFunction;
}

可以看出RequestFactory中有一个参数表示是否是协程请求,这个参数是在build过程中确定的,是根据最后一个参数是否为Continuation确定的。

再回头看看解析过程HttpServiceMethod.parseAnnotations的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
        Retrofit retrofit, Method method, RequestFactory requestFactory) {
        boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
        boolean continuationWantsResponse = false;
        boolean continuationBodyNullable = false;

        Annotation[] annotations = method.getAnnotations();
        Type adapterType;
        if (isKotlinSuspendFunction) {
            // 这里会包装成为type为Call
            adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
            // 因为协程不需要切换线程,所以确保有跳过注解
            annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
        } else {
            adapterType = method.getGenericReturnType();
        }

        // 创建Retrofit的通用CallAdapter,由CallAdapter.Factory创建,返回的是由DefaultCallAdapterFactory创建的匿名内部类
        CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations);
        Type responseType = callAdapter.responseType();

        // ...

        // 创建解析器
        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>)
            // 区别是是否需要Response来包裹
            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);
        }
    }

主要是创建CallAdapter,这里会根据是是否是协程生成普通的HttpServiceMethod还是SuspendForBody这个HttpServiceMethod。关系如下:

请求部分

首先线看一下Call的实现类有哪些

这里有两个类都实现了Retrofit的Call接口,ExecutorCallbackCall主要是一个代理类,为需要返回结果线程调度的类提供一个线程调度。而OkHttpCall才是真正对OkHttp的Call的封装,里面实现了真正的Call的创建和请求等。

然后再看一下创建方法时候的动态代理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T)
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {
                // ...
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
                }
            });
}

在方法解析完成后直接调用了其的invoke方法

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

invoke方法创建了真正的OkHttpCall

最后再调用了adapt方法,这里因为HttpServiceMethod的具体实现类不同,所以后面再看

普通请求

接着一般请求的adapt方法

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

这个callAdapter由Retrofit创建,实际是由DefaultCallAdapterFactory返回的一个匿名内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
    private final @Nullable Executor callbackExecutor;

    DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
        this.callbackExecutor = callbackExecutor;
    }

    @Override
    public @Nullable CallAdapter<?, ?> get(
        Type returnType, Annotation[] annotations, Retrofit retrofit) {
        // 对call的代理,主要是返回结果切换线程
        if (getRawType(returnType) != Call.class) {
            return null;
        }
        
        final Executor executor =
            Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
                ? null
                : callbackExecutor;

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

    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) {
        Objects.requireNonNull(callback, "callback == null");

        delegate.enqueue(
            new Callback<T>() {
                @Override
                public void onResponse(Call<T> call, final Response<T> response) {
                    callbackExecutor.execute(
                        () -> {
                            // 切换回主线程
                            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(() -> callback.onFailure(ExecutorCallbackCall.this, t));
                }
            });
        }

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

这里的代码比较简单,DefaultCallAdapterFactory内部传入了callbackExecutor来对结果切换线程,在安卓中是主线程的调度器。adapt方法在调用时会根据是否需要切换线程,然后默认的call对象还是有线程调度的call代理。

1
2
3
4
5
6
7
8
static final class MainThreadExecutor implements Executor {
    private final Handler handler = new Handler(Looper.getMainLooper());

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

使用handler来制作的调度器。

execute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public Response<T> execute() throws IOException {
    okhttp3.Call call;

    synchronized (this) {
        if (executed) throw new IllegalStateException("Already executed.");
        executed = true;
        // 这里会创建一个真正的call请求对象
        call = getRawCall();
    }

    if (canceled) {
        call.cancel();
    }
    // 触发请求并解析结果
    return parseResponse(call.execute());
}

因为阻塞请求是不需要调度的,所有跳过那款代码,直接看最后的部分。流程比较简单,就是创建一个OkHttp的call对象,然后执行请求,最后解析结果。

enqueue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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) {
                Response<T> response;
                try {
                    response = parseResponse(rawResponse);
                } catch (Throwable e) {
                    throwIfFatal(e);
                    callFailure(e);
                    return;
                }

                try {
                    callback.onResponse(OkHttpCall.this, response);
                } catch (Throwable t) {
                    throwIfFatal(t);
                    t.printStackTrace(); // TODO this is not great
                }
            }

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

            private void callFailure(Throwable e) {
                try {
                    callback.onFailure(OkHttpCall.this, e);
                } catch (Throwable t) {
                    throwIfFatal(t);
                    t.printStackTrace(); // TODO this is not great
                }
            }
        });
}

和同步请求类似,都是创建OkHttp的Call对象,利用OkHttp来发起真正的请求。

协程请求

协程请求不同与普通的请求,因为方法调用时结果就返回了,而不是通过Call去调用的。所以实际的请求是在解析后直接执行的,那么奥秘就是在adapt方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    @Override
    protected Object adapt(Call<ResponseT> call, Object[] args) {
      call = callAdapter.adapt(call);

      Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];

      try {
        return isNullable
            ? KotlinExtensions.awaitNullable(call, continuation)
            : KotlinExtensions.await(call, continuation);
      } catch (Exception e) {
        return KotlinExtensions.suspendAndThrow(e, continuation);
      }
    }

可以看出一样是使用callAdapter来构建出真实的请求类,实际返回的也是他本身,然后使用协程启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
suspend fun <T : Any> Call<T>.await(): T {
  return suspendCancellableCoroutine { continuation ->
    continuation.invokeOnCancellation {
      cancel()
    }
    // 调用call的enqueue方法启动
    enqueue(object : Callback<T> {
        override fun onResponse(call: Call<T>, response: Response<T>) {
            if (response.isSuccessful) {
                val body = response.body()
                if (body == null) {
                    val invocation = call.request().tag(Invocation::class.java)!!
                    val method = invocation.method()
                    val e = KotlinNullPointerException("Response from " +
                        method.declaringClass.name +
                        '.' +
                        method.name +
                        " was null but response body type was declared as non-null")
                    continuation.resumeWithException(e)
                } else {
                    continuation.resume(body)
                }
            } else {
                continuation.resumeWithException(HttpException(response))
            }
        }

        override fun onFailure(call: Call<T>, t: Throwable) {
            continuation.resumeWithException(t)
        }
    })
  }
}

这里和正常的协程调用的是一个enqueue,只是这里并不包含切换线程的代理。

总结:

  • Retrofit在create方法时,会为接口生成一个动态代理,在方法调用时开始解析方法。
  • 解析过程会根据参数是否有Continution区分是否是协程请求,过程会得到一个由DefaultCallAdapter创建的CallAdapter对象,然后构建到解析好的方法中。CallAdapter主要用作适配是否需要线程调度。默认调度器是使用Handler实现的。
  • 创建的方法返回后,会触发invoke方法,方法中都会创建一个OkHttpCall对象,这个对象是真正对Okhttp的Call的封装。然后使用之前的调度器CallAdapter进行代理返回一个被代理的Call。
  • 方法的触发就是直接调用Call的方法,然后在对应的方法中会执行OkHttp Call的构建以及真实的请求,对请求回来的数据然后进行解析。对于协程来说,在解析方法后的invoke转换中就会利用Call的扩展函数来进行协程的请求。
This post is licensed under CC BY 4.0 by the author.