• Retrofit 在kotlin中使用及解析


    build.gradle

    1. dependencies {
    2. //retrofit + gson + rxjava
    3. implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    4. implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    5. implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
    6. }
    1. interface ApiService {
    2. @GET("getUserData")
    3. fun getUserData1(): Call
    4. }
    5. fun main1() {
    6. val retrofit = Retrofit.Builder()
    7. .baseUrl("http://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
    8. .build()
    9. val service = retrofit.create(ApiService::class.java)
    10. val call: Call = service.getUserData1()
    11. call.enqueue(object : Callback {
    12. override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
    13. val userBean = response.body()?.string()
    14. println("userBean: $userBean")
    15. }
    16. override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
    17. println("onFailure: $t")
    18. }
    19. })
    20. }

    Retrofit 是建立在 OkHttp 之上的一个网络请求封装库,内部依靠 okhttp 来完成实际网络请求。Retrofit 在使用上很简洁, API 通过 interface 来声明。我只需要通过 interface 来声明 API路径、请求方式、请求参数、返回值类型等各个配置项。

    可以看到,getUserData() 方法的请求结果是一个 json 格式的字符串,其返回值类型被定义为 Call , 此处的 ResponseBody 即 okHttp3.ResponseBody ,是 okhttp 提供的对网络请求结果的包装类,Call 即 retrofit2.Call ,是 Retrofit 对 okhttp3.Call 做的一层包装,OkHttp在实际发起请求的时候使用的回调是 okhttp3.Call ,回调内部会中转调用 retrofit2.Call,以便将请求结果转交给外部。

    1、converter-gson

    API 返回值 Json 转换 :

    1. interface ApiService {
    2. @GET("getUserData")
    3. fun getUserData2(): Call
    4. }
    5. data class UserBean(val status: Int, val msg: String, val data: Data)
    6. data class Data(val userName: String, val userAge: Long)
    7. fun main2() {
    8. val retrofit = Retrofit.Builder()
    9. .baseUrl("http://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
    10. .addConverterFactory(GsonConverterFactory.create())
    11. .build()
    12. val service = retrofit.create(ApiService::class.java)
    13. val call: Call = service.getUserData2()
    14. call.enqueue(object : Callback {
    15. override fun onResponse(call: Call<UserBean>, response: Response<UserBean>) {
    16. val userBean = response.body()
    17. println("userBean: $userBean")
    18. }
    19. override fun onFailure(call: Call<UserBean>, t: Throwable) {
    20. println("onFailure: $t")
    21. }
    22. })
    23. }

    2、adapter-rxjava2

    adapter-rxjava2 转换返回值为被观察者

    1. interface ApiService {
    2. @GET("getUserData")
    3. fun getUserData3(): Observable
    4. }
    5. data class UserBean(val status: Int, val msg: String, val data: Data)
    6. data class Data(val userName: String, val userAge: Long)
    7. fun main3() {
    8. val retrofit = Retrofit.Builder()
    9. .baseUrl("http://mockapi.eolinker.com/9IiwI82f58c23411240ed608ceca204b2f185014507cbe3/")
    10. .addConverterFactory(GsonConverterFactory.create())
    11. .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    12. .build()
    13. val service = retrofit.create(ApiService::class.java)
    14. val call: Observable = service.getUserData3()
    15. call.subscribe({ user ->
    16. println("userBean: $user")
    17. }, { t ->
    18. println("onFailure: $t")
    19. })
    20. }

    一、Retrofit请求过程 

    1、Retrofit.create()

    1. @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
    2. public T create(final Class service) {
    3. validateServiceInterface(service);
    4. return (T)
    5. Proxy.newProxyInstance(
    6. service.getClassLoader(),
    7. new Class[] {service},
    8. new InvocationHandler() {
    9. private final Platform platform = Platform.get();
    10. private final Object[] emptyArgs = new Object[0];
    11. @Override
    12. public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
    13. throws Throwable {
    14. // If the method is a method from Object then defer to normal invocation.
    15. if (method.getDeclaringClass() == Object.class) {
    16. return method.invoke(this, args);
    17. }
    18. args = args != null ? args : emptyArgs;
    19. return platform.isDefaultMethod(method)
    20. ? platform.invokeDefaultMethod(method, service, proxy, args)
    21. : loadServiceMethod(method).invoke(args);
    22. }
    23. });
    24. }
    1. ServiceMethod loadServiceMethod(Method method) {
    2. ServiceMethod result = serviceMethodCache.get(method);
    3. if (result != null) return result;
    4. synchronized (serviceMethodCache) {
    5. result = serviceMethodCache.get(method);
    6. if (result == null) {
    7. result = ServiceMethod.parseAnnotations(this, method);
    8. serviceMethodCache.put(method, result);
    9. }
    10. }
    11. return result;
    12. }

     

    2、ServiceMethod 

    1. abstract class ServiceMethod<T> {
    2. static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {
    3. RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    4. Type returnType = method.getGenericReturnType();
    5. if (Utils.hasUnresolvableType(returnType)) {
    6. throw methodError(
    7. method,
    8. "Method return type must not include a type variable or wildcard: %s",
    9. returnType);
    10. }
    11. if (returnType == void.class) {
    12. throw methodError(method, "Service methods cannot return void.");
    13. }
    14. return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
    15. }
    16. abstract @Nullable T invoke(Object[] args);
    17. }

     3、HttpServiceMethod

    1. abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
    2. static HttpServiceMethod parseAnnotations(
    3. Retrofit retrofit, Method method, RequestFactory requestFactory) {
    4. boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    5. boolean continuationWantsResponse = false;
    6. boolean continuationBodyNullable = false;
    7. Annotation[] annotations = method.getAnnotations();
    8. Type adapterType;
    9. if (isKotlinSuspendFunction) {
    10. Type[] parameterTypes = method.getGenericParameterTypes();
    11. Type responseType =
    12. Utils.getParameterLowerBound(
    13. 0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
    14. if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
    15. // Unwrap the actual body type from Response.
    16. responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
    17. continuationWantsResponse = true;
    18. } else {
    19. // TODO figure out if type is nullable or not
    20. // Metadata metadata = method.getDeclaringClass().getAnnotation(Metadata.class)
    21. // Find the entry for method
    22. // Determine if return type is nullable or not
    23. }
    24. adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
    25. annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    26. } else {
    27. adapterType = method.getGenericReturnType();
    28. }
    29. CallAdapter callAdapter =
    30. createCallAdapter(retrofit, method, adapterType, annotations);
    31. Type responseType = callAdapter.responseType();
    32. if (responseType == okhttp3.Response.class) {
    33. throw methodError(
    34. method,
    35. "'"
    36. + getRawType(responseType).getName()
    37. + "' is not a valid response body type. Did you mean ResponseBody?");
    38. }
    39. if (responseType == Response.class) {
    40. throw methodError(method, "Response must include generic type (e.g., Response)");
    41. }
    42. // TODO support Unit for Kotlin?
    43. if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
    44. throw methodError(method, "HEAD method must use Void as response type.");
    45. }
    46. Converter responseConverter =
    47. createResponseConverter(retrofit, method, responseType);
    48. okhttp3.Call.Factory callFactory = retrofit.callFactory;
    49. if (!isKotlinSuspendFunction) {
    50. return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    51. } else if (continuationWantsResponse) {
    52. //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    53. return (HttpServiceMethod)
    54. new SuspendForResponse<>(
    55. requestFactory,
    56. callFactory,
    57. responseConverter,
    58. (CallAdapter>) callAdapter);
    59. } else {
    60. //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
    61. return (HttpServiceMethod)
    62. new SuspendForBody<>(
    63. requestFactory,
    64. callFactory,
    65. responseConverter,
    66. (CallAdapter>) callAdapter,
    67. continuationBodyNullable);
    68. }
    69. }
    70. @Override
    71. final @Nullable ReturnT invoke(Object[] args) {
    72. Call call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    73. return adapt(call, args);
    74. }
    75. }

     4、OkHttpCall

    OkHttpCall 是实际发起 okHttp 请求的地方。当我们调用 fun getUserData(): Call 方法时,返回的 Call 对象实际上是 OkHttp 类型。

    1. final class OkHttpCall<T> implements Call<T> {
    2. private final RequestFactory requestFactory;
    3. private final Object[] args;
    4. private final okhttp3.Call.Factory callFactory;
    5. private final Converter responseConverter;
    6. private volatile boolean canceled;
    7. @GuardedBy("this")
    8. private @Nullable okhttp3.Call rawCall;
    9. @GuardedBy("this") // Either a RuntimeException, non-fatal Error, or IOException.
    10. private @Nullable Throwable creationFailure;
    11. @GuardedBy("this")
    12. private boolean executed;
    13. OkHttpCall(
    14. RequestFactory requestFactory,
    15. Object[] args,
    16. okhttp3.Call.Factory callFactory,
    17. Converter responseConverter) {
    18. this.requestFactory = requestFactory;
    19. this.args = args;
    20. this.callFactory = callFactory;
    21. this.responseConverter = responseConverter;
    22. }
    23. @SuppressWarnings("CloneDoesntCallSuperClone") // We are a final type & this saves clearing state.
    24. @Override
    25. public OkHttpCall clone() {
    26. return new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    27. }
    28. @Override
    29. public synchronized Request request() {
    30. try {
    31. return getRawCall().request();
    32. } catch (IOException e) {
    33. throw new RuntimeException("Unable to create request.", e);
    34. }
    35. }
    36. @Override
    37. public synchronized Timeout timeout() {
    38. try {
    39. return getRawCall().timeout();
    40. } catch (IOException e) {
    41. throw new RuntimeException("Unable to create call.", e);
    42. }
    43. }
    44. /**
    45. * Returns the raw call, initializing it if necessary. Throws if initializing the raw call throws,
    46. * or has thrown in previous attempts to create it.
    47. */
    48. @GuardedBy("this")
    49. private okhttp3.Call getRawCall() throws IOException {
    50. okhttp3.Call call = rawCall;
    51. if (call != null) return call;
    52. // Re-throw previous failures if this isn't the first attempt.
    53. if (creationFailure != null) {
    54. if (creationFailure instanceof IOException) {
    55. throw (IOException) creationFailure;
    56. } else if (creationFailure instanceof RuntimeException) {
    57. throw (RuntimeException) creationFailure;
    58. } else {
    59. throw (Error) creationFailure;
    60. }
    61. }
    62. // Create and remember either the success or the failure.
    63. try {
    64. return rawCall = createRawCall();
    65. } catch (RuntimeException | Error | IOException e) {
    66. throwIfFatal(e); // Do not assign a fatal error to creationFailure.
    67. creationFailure = e;
    68. throw e;
    69. }
    70. }
    71. @Override
    72. public void enqueue(final Callback callback) {
    73. Objects.requireNonNull(callback, "callback == null");
    74. okhttp3.Call call;
    75. Throwable failure;
    76. synchronized (this) {
    77. if (executed) throw new IllegalStateException("Already executed.");
    78. executed = true;
    79. call = rawCall;
    80. failure = creationFailure;
    81. if (call == null && failure == null) {
    82. try {
    83. call = rawCall = createRawCall();
    84. } catch (Throwable t) {
    85. throwIfFatal(t);
    86. failure = creationFailure = t;
    87. }
    88. }
    89. }
    90. if (failure != null) {
    91. callback.onFailure(this, failure);
    92. return;
    93. }
    94. if (canceled) {
    95. call.cancel();
    96. }
    97. call.enqueue(
    98. new okhttp3.Callback() {
    99. @Override
    100. public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
    101. Response response;
    102. try {
    103. response = parseResponse(rawResponse);
    104. } catch (Throwable e) {
    105. throwIfFatal(e);
    106. callFailure(e);
    107. return;
    108. }
    109. try {
    110. callback.onResponse(OkHttpCall.this, response);
    111. } catch (Throwable t) {
    112. throwIfFatal(t);
    113. t.printStackTrace(); // TODO this is not great
    114. }
    115. }
    116. @Override
    117. public void onFailure(okhttp3.Call call, IOException e) {
    118. callFailure(e);
    119. }
    120. private void callFailure(Throwable e) {
    121. try {
    122. callback.onFailure(OkHttpCall.this, e);
    123. } catch (Throwable t) {
    124. throwIfFatal(t);
    125. t.printStackTrace(); // TODO this is not great
    126. }
    127. }
    128. });
    129. }
    130. @Override
    131. public synchronized boolean isExecuted() {
    132. return executed;
    133. }
    134. @Override
    135. public Response execute() throws IOException {
    136. okhttp3.Call call;
    137. synchronized (this) {
    138. if (executed) throw new IllegalStateException("Already executed.");
    139. executed = true;
    140. call = getRawCall();
    141. }
    142. if (canceled) {
    143. call.cancel();
    144. }
    145. return parseResponse(call.execute());
    146. }
    147. }

     5、RequestFactory

    1. final class RequestFactory {
    2. static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    3. return new Builder(retrofit, method).build();
    4. }
    5. private final Method method;
    6. private final HttpUrl baseUrl;
    7. final String httpMethod;
    8. private final @Nullable String relativeUrl;
    9. private final @Nullable Headers headers;
    10. private final @Nullable MediaType contentType;
    11. private final boolean hasBody;
    12. private final boolean isFormEncoded;
    13. private final boolean isMultipart;
    14. private final ParameterHandler[] parameterHandlers;
    15. final boolean isKotlinSuspendFunction;
    16. RequestFactory(Builder builder) {
    17. method = builder.method;
    18. baseUrl = builder.retrofit.baseUrl;
    19. httpMethod = builder.httpMethod;
    20. relativeUrl = builder.relativeUrl;
    21. headers = builder.headers;
    22. contentType = builder.contentType;
    23. hasBody = builder.hasBody;
    24. isFormEncoded = builder.isFormEncoded;
    25. isMultipart = builder.isMultipart;
    26. parameterHandlers = builder.parameterHandlers;
    27. isKotlinSuspendFunction = builder.isKotlinSuspendFunction;
    28. }
    29. okhttp3.Request create(Object[] args) throws IOException {
    30. @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    31. ParameterHandler[] handlers = (ParameterHandler[]) parameterHandlers;
    32. int argumentCount = args.length;
    33. if (argumentCount != handlers.length) {
    34. throw new IllegalArgumentException(
    35. "Argument count ("
    36. + argumentCount
    37. + ") doesn't match expected count ("
    38. + handlers.length
    39. + ")");
    40. }
    41. RequestBuilder requestBuilder =
    42. new RequestBuilder(
    43. httpMethod,
    44. baseUrl,
    45. relativeUrl,
    46. headers,
    47. contentType,
    48. hasBody,
    49. isFormEncoded,
    50. isMultipart);
    51. if (isKotlinSuspendFunction) {
    52. // The Continuation is the last parameter and the handlers array contains null at that index.
    53. argumentCount--;
    54. }
    55. List argumentList = new ArrayList<>(argumentCount);
    56. for (int p = 0; p < argumentCount; p++) {
    57. argumentList.add(args[p]);
    58. handlers[p].apply(requestBuilder, args[p]);
    59. }
    60. return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
    61. }
    62. /**
    63. * Inspects the annotations on an interface method to construct a reusable service method. This
    64. * requires potentially-expensive reflection so it is best to build each service method only once
    65. * and reuse it. Builders cannot be reused.
    66. */
    67. static final class Builder {
    68. // Upper and lower characters, digits, underscores, and hyphens, starting with a character.
    69. private static final String PARAM = "[a-zA-Z][a-zA-Z0-9_-]*";
    70. private static final Pattern PARAM_URL_REGEX = Pattern.compile("\\{(" + PARAM + ")\\}");
    71. private static final Pattern PARAM_NAME_REGEX = Pattern.compile(PARAM);
    72. final Retrofit retrofit;
    73. final Method method;
    74. final Annotation[] methodAnnotations;
    75. final Annotation[][] parameterAnnotationsArray;
    76. final Type[] parameterTypes;
    77. boolean gotField;
    78. boolean gotPart;
    79. boolean gotBody;
    80. boolean gotPath;
    81. boolean gotQuery;
    82. boolean gotQueryName;
    83. boolean gotQueryMap;
    84. boolean gotUrl;
    85. @Nullable String httpMethod;
    86. boolean hasBody;
    87. boolean isFormEncoded;
    88. boolean isMultipart;
    89. @Nullable String relativeUrl;
    90. @Nullable Headers headers;
    91. @Nullable MediaType contentType;
    92. @Nullable Set relativeUrlParamNames;
    93. @Nullable ParameterHandler[] parameterHandlers;
    94. boolean isKotlinSuspendFunction;
    95. Builder(Retrofit retrofit, Method method) {
    96. this.retrofit = retrofit;
    97. this.method = method;
    98. this.methodAnnotations = method.getAnnotations();
    99. this.parameterTypes = method.getGenericParameterTypes();
    100. this.parameterAnnotationsArray = method.getParameterAnnotations();
    101. }
    102. RequestFactory build() {
    103. for (Annotation annotation : methodAnnotations) {
    104. parseMethodAnnotation(annotation);
    105. }
    106. if (httpMethod == null) {
    107. throw methodError(method, "HTTP method annotation is required (e.g., @GET, @POST, etc.).");
    108. }
    109. if (!hasBody) {
    110. if (isMultipart) {
    111. throw methodError(
    112. method,
    113. "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    114. }
    115. if (isFormEncoded) {
    116. throw methodError(
    117. method,
    118. "FormUrlEncoded can only be specified on HTTP methods with "
    119. + "request body (e.g., @POST).");
    120. }
    121. }
    122. int parameterCount = parameterAnnotationsArray.length;
    123. parameterHandlers = new ParameterHandler[parameterCount];
    124. for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
    125. parameterHandlers[p] =
    126. parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
    127. }
    128. if (relativeUrl == null && !gotUrl) {
    129. throw methodError(method, "Missing either @%s URL or @Url parameter.", httpMethod);
    130. }
    131. if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
    132. throw methodError(method, "Non-body HTTP method cannot contain @Body.");
    133. }
    134. if (isFormEncoded && !gotField) {
    135. throw methodError(method, "Form-encoded method must contain at least one @Field.");
    136. }
    137. if (isMultipart && !gotPart) {
    138. throw methodError(method, "Multipart method must contain at least one @Part.");
    139. }
    140. return new RequestFactory(this);
    141. }
    142. }
    143.  

      参考:

      https://github.com/leavesCZY/AndroidGuide/blob/master/%E4%B8%BB%E6%B5%81%E5%BC%80%E6%BA%90%E5%BA%93%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%887%EF%BC%89Retrofit%20%E6%BA%90%E7%A0%81%E8%AF%A6%E8%A7%A3.md

    144. 相关阅读:
      HarmonyOS开发 - hilog日志系统
      摸鱼时,我用几百张字符画做了个动画......
      strerror函数详解 看这一篇就够了-C语言(函数讲解、 使用用法举例、作用)
      《lwip学习5》-- lwip一探究竟
      Hashtable 相关面试集合(2022)
      深入探索SDL游戏开发
      什么是Helm?它是如何提升云原生应用私有化部署效率的
      ARM体系结构与汇编语言
      经济管理专业必备的15种国内数据库推荐
      Pycharm中配置不了conda解释器
    145. 原文地址:https://blog.csdn.net/u010498248/article/details/132617235