常用网络请求框架Retrofit学习笔记

一、retrofit使用解析

        请看图:

1.1 引用retrofit

implementation('com.squareup.retrofit2:retrofit:2.9.0')

        retrofit框架请求库是retrofit自家的okhttp,仓库依赖的是okhttp3.x,所以如果你想用okhttp4.x则需要重新引用okhttp4.x。okhttp也是依赖自家的okio。

implementation("com.squareup.okhttp3:okhttp:4.11.0")

1.2 创建Retrofit实例

1.2.1 Retrofit
Retrofit(
    okhttp3.Call.Factory callFactory,
    HttpUrl baseUrl,
    List<Converter.Factory> converterFactories,
    List<CallAdapter.Factory> callAdapterFactories,
    @Nullable Executor callbackExecutor,
    boolean validateEagerly)

        这是Retrofit的构造方法,同时Retrofit也提供了Builder来帮助构造,那么我们直接看Builder.build()方法,可以知道baseUrl是必填项,其余都是可选项。

    /**
     * Create the {@link Retrofit} instance using the configured values.
     *
     * <p>Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
     * OkHttpClient} will be created and used.
     */
    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();
      }

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

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

      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      converterFactories.addAll(platform.defaultConverterFactories());

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

        所以最简单的创建实例方法即是以下代码:

Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build()
1.2.2 OkHttpClient

        为了代码复用性,比如统一请求头处理、统一加解密处理、统一日志处理等都要求我们配置新的OkHttpClient,因为这样我们才能用okhttp的拦截器来实现统一处理,然后通过Retrofit.Builder().client(OkHttpClient)传入配置。当然如果有更深入的改造甚至可以写一个callFactory传给Builder,retrofit或者okhttp在扩展性方面是做的非常好的。Retrofit默认使用OkHttpClient无参构造方法,如果要添加诸多配置需要使用OkHttpClient.Builder传参构造。

implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
val okHttpClient = OkHttpClient.Builder()
    .addInterceptor(HeaderInterceptor())
    .addInterceptor(HttpLoggingInterceptor())
    .addInterceptor(EncryptInterceptor())
    .addInterceptor(DecryptInterceptor())
    .readTimeout(60, TimeUnit.SECONDS)
    .writeTimeout(60, TimeUnit.SECONDS)
    .build()
Retrofit.Builder()
    .client(okHttpClient)
    .baseUrl("https://api.github.com/")
    .addConverterFactory(FastJsonConverterFactory.create())
    .build()

         注意:logging-interceptor和okhttp最好保持一致,否则可能出现No virtual method log(ILjava/lang/String;Ljava/lang/Throwable;)。

        有没有注意到retrofit创建实例多了一个addConverterFactory?下面我们来看下它的作用。

1.2.3 Converter
public interface Converter<F, T> {
  @Nullable
  T convert(F value) throws IOException;

  abstract class Factory {

    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
        Type type, Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    public @Nullable Converter<?, RequestBody> requestBodyConverter(
        Type type,
        Annotation[] parameterAnnotations,
        Annotation[] methodAnnotations,
        Retrofit retrofit) {
      return null;
    }
  }
}

        为什么需要这个Converter呢?从上面的方法中我们可以看出这就是RequestBody或者ResponseBody的转换。

        首先我们应该知道,retrofit用的是okhttp发起请求的,那么okhttp的请求类是什么呢?

class Request internal constructor(
  @get:JvmName("url") val url: HttpUrl,
  @get:JvmName("method") val method: String,
  @get:JvmName("headers") val headers: Headers,
  @get:JvmName("body") val body: RequestBody?,
  internal val tags: Map<Class<*>, Any>
)

         当我们的请求body不为空的时候,创建Request需要传入RequestBody,而okhttp是retrofit持有的,也就是说RequestBody是由retrofit创建的,这时候我们应该想到Body注解,Body注解可以让我们传入自己定义的数据结构,但是这个数据结构retrofit不一定能识别出来,怎么办呢?Converter说一切有我,这就是Converter.requestBodyConverter方法实现的这一步骤。常用的数据结构retrofit已经提供了相应的Converter:

  • Gsoncom.squareup.retrofit2:converter-gson
  • Jacksoncom.squareup.retrofit2:converter-jackson
  • Moshicom.squareup.retrofit2:converter-moshi
  • Protobufcom.squareup.retrofit2:converter-protobuf
  • Wirecom.squareup.retrofit2:converter-wire
  • Simple XMLcom.squareup.retrofit2:converter-simplexml
  • JAXBcom.squareup.retrofit2:converter-jaxb
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

        国内fastjson也有相应的Converter,相信GitHub上总能找到你需要的。fastjson2出现了,我们大概只需要将converter-fastjson-android的类替换一下fastjson2的包名就行了。

implementation('org.ligboy.retrofit2:converter-fastjson-android:2.1.0')

        ResponseBody也是同理,ResponseBody是okhttp提供的,我们通常需要将之转换成我们方便使用的数据结构,所以还是由Converter出手解决。 

        那么能不能添加多个Converter呢?我们先从retrofit的角度看看:

public <T> Converter<T, RequestBody> nextRequestBodyConverter(
      @Nullable Converter.Factory skipPast,
      Type type,
      Annotation[] parameterAnnotations,
      Annotation[] methodAnnotations) {
    Objects.requireNonNull(type, "type == null");
    Objects.requireNonNull(parameterAnnotations, "parameterAnnotations == null");
    Objects.requireNonNull(methodAnnotations, "methodAnnotations == null");

    int start = converterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = converterFactories.size(); i < count; i++) {
      Converter.Factory factory = converterFactories.get(i);
      Converter<?, RequestBody> converter =
          factory.requestBodyConverter(type, parameterAnnotations, methodAnnotations, this);
      if (converter != null) {
        //noinspection unchecked
        return (Converter<T, RequestBody>) converter;
      }
    }
    ......
  }

         答案是可以的,但是前面的Converter一旦匹配正确就return了,所以我们不要添加相同类型的Converter,这是无效的,当然我们也不会有这种需求。但是添加多个Converter还有一个前提就是前面的Converter在遇到自身不匹配的数据类型时requestBodyConverter/responseBodyConverter需要return null,否则也是无法再执行下一个Converter的,比如converter-fastjson就没有return null,导致无法在其后再添加Converter了。

1.2.4 CallAdapter

1.3 定义接口

       retrofit定义了很多注解来快速定义接口,下面将简单介绍GET和POST的定义。

1.3.1 GET

1. 请求路径:baseUrl/xxx/xxx

@GET("xxx/xxx")
suspend fun getPages(): ResponseBody

2. 请求路径:baseUrl/xxx/xxx/page

@GET("xxx/xxx/{page}")
suspend fun getPages(@Path("page") page: String): ResponseBody 

3. 请求路径:baseUrl/xxx/xxx?page= 

@GET("xxx/xxx")
suspend fun getPages(@Query("page") page: String): ResponseBody 

4.  请求路径:baseUrl/xxx/xxx?page=page&pageSize=pageSize

@GET("xxx/xxx") suspend fun getPages(@Query("page") page: String, @Query("pageSize") pageSize: String): ResponseBody 

@GET("xxx/xxx")
suspend fun getPages(@QueryMap map: HashMap<String, String>): ResponseBody

5. 请求路径: baseUrl/xxx/xxx?urlencode(name)

        注意:后台收到urlencode(name)后能否正常转码。

@GET("xxx/xxx") suspend fun getPages(@QueryName(encoded = true) name: String): ResponseBody 

6. 下载:baseUrl/xxx

@Streaming
@GET
suspend fun download(@Url url: String): retrofit2.Response<ResponseBody> 

1.3.2 POST

1. 请求路径:baseUrl/xxx

@FormUrlEncoded

@POST("xxx")

suspend fun getPages(@Field("page") page: String): ResponseBody


 

@FormUrlEncoded

@POST("xxx")

suspend fun getPages(@FieldMap map: HashMap<String, String>): ResponseBody

 2. 请求路径:baseUrl/xxx

        如果要传入@Body,需要提供转换器给retrofit,即addConverterFactory()。

@POST("xxx")

suspend fun getPages(@Body body: JSONObject): ResponseBody

3. 上传:baseUrl/xxx

@MultiPart
@POST("xxx")
suspend fun logs(@Part parts: List<MultipartBody.Part>): ResponseBody 

1.4 调用接口

 1.4.1 接口调用

mRetrofit.create(HttpService::class.java).getPages()

        我们知道这个接口方法是通过注解是定义的,那么okhttp是如何开始请求的呢?首先我们注意retrofit.create方法:

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

         Retrofit.create主要创建HttpService的代理,通过代理执行方法。看loadServiceMethod:

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

         Service代理 invoke结果是由ServiceMethod.invoke提供,接着再看ServiceMethod:

abstract class ServiceMethod<T> {
  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.");
    }

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

         这是一个抽象类,parseAnnotations是将注解解析处理,invoke是执行请求方法,这里有两个类需要注意:RequestFactory和HttpServiceMethod,以下是RequestFactory:

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
  return new Builder(retrofit, method).build();
}
RequestFactory(Builder builder) {
  method = builder.method;
  baseUrl = builder.retrofit.baseUrl;
  httpMethod = builder.httpMethod;
  relativeUrl = builder.relativeUrl;
  headers = builder.headers;
  contentType = builder.contentType;
  hasBody = builder.hasBody;
  isFormEncoded = builder.isFormEncoded;
  isMultipart = builder.isMultipart;
  parameterHandlers = builder.parameterHandlers;
  isKotlinSuspendFunction = builder.isKotlinSuspendFunction;
}

        从RequestFactory属性基本可以确认注解都在该类完成解析处理了。我们在RequestFactory类的Structure中看到create() 返回了okhttp3.Request,创建了Request就可以通过OkHttpClient.newCall(request).execute()发起请求了。

okhttp3.Request create(Object[] args) throws IOException {
    @SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
    ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

    int argumentCount = args.length;
    if (argumentCount != handlers.length) {
      throw new IllegalArgumentException(
          "Argument count ("
              + argumentCount
              + ") doesn't match expected count ("
              + handlers.length
              + ")");
    }

    RequestBuilder requestBuilder =
        new RequestBuilder(
            httpMethod,
            baseUrl,
            relativeUrl,
            headers,
            contentType,
            hasBody,
            isFormEncoded,
            isMultipart);

    if (isKotlinSuspendFunction) {
      // The Continuation is the last parameter and the handlers array contains null at that index.
      argumentCount--;
    }

    List<Object> argumentList = new ArrayList<>(argumentCount);
    for (int p = 0; p < argumentCount; p++) {
      argumentList.add(args[p]);
      handlers[p].apply(requestBuilder, args[p]);
    }

    return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
  }

        而后RequestFactory作为参数传给了HttpServiceMethod.parseAnnotations(),同时HttpServiceMethod继承于ServiceMethod,那么invoke发起请求实现就在此类了,请求过程基本都可以在这里找到了,毕竟Service代理执行完invoke就结束了。

HttpServiceMethod(
      RequestFactory requestFactory,
      okhttp3.Call.Factory callFactory,
      Converter<ResponseBody, ResponseT> responseConverter) {
    this.requestFactory = requestFactory;
    this.callFactory = callFactory;
    this.responseConverter = responseConverter;
  }

......

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

protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

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

         adapt是一个抽象方法,那就有实现类,有三个:CallAdapted、SuspendForResponse、SuspendForBody,根据不同类型使用不同的adapt,最终发起okhttp请求,返回响应数据,再将响应数据转换成我们想要的类型。

 1.4.2 OkHttpClient请求执行
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)


override fun execute(): Response {
  check(executed.compareAndSet(false, true)) { "Already Executed" }

  timeout.enter()
  callStart()
  try {
    client.dispatcher.executed(this)
    return getResponseWithInterceptorChain()
  } finally {
    client.dispatcher.finished(this)
  }
}


internal fun getResponseWithInterceptorChain(): Response {
  // Build a full stack of interceptors.
  val interceptors = mutableListOf<Interceptor>()
  interceptors += client.interceptors
  interceptors += RetryAndFollowUpInterceptor(client)
  interceptors += BridgeInterceptor(client.cookieJar)
  interceptors += CacheInterceptor(client.cache)
  interceptors += ConnectInterceptor
  if (!forWebSocket) {
    interceptors += client.networkInterceptors
  }
  interceptors += CallServerInterceptor(forWebSocket)

  val chain = RealInterceptorChain(
      call = this,
      interceptors = interceptors,
      index = 0,
      exchange = null,
      request = originalRequest,
      connectTimeoutMillis = client.connectTimeoutMillis,
      readTimeoutMillis = client.readTimeoutMillis,
      writeTimeoutMillis = client.writeTimeoutMillis
  )

  var calledNoMoreExchanges = false
  try {
    val response = chain.proceed(originalRequest)
    if (isCanceled()) {
      response.closeQuietly()
      throw IOException("Canceled")
    }
    return response
  } catch (e: IOException) {
    calledNoMoreExchanges = true
    throw noMoreExchanges(e) as Throwable
  } finally {
    if (!calledNoMoreExchanges) {
      noMoreExchanges(null)
    }
  }
}
1.4.3 RealInterceptorChain拦截器责任链
override fun proceed(request: Request): Response {
    check(index < interceptors.size)

    calls++

    if (exchange != null) {
      check(exchange.finder.sameHostAndPort(request.url)) {
        "network interceptor ${interceptors[index - 1]} must retain the same host and port"
      }
      check(calls == 1) {
        "network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
      }
    }

    // Call the next interceptor in the chain.
    val next = copy(index = index + 1, request = request)
    val interceptor = interceptors[index]

    @Suppress("USELESS_ELVIS")
    val response = interceptor.intercept(next) ?: throw NullPointerException(
        "interceptor $interceptor returned null")

    if (exchange != null) {
      check(index + 1 >= interceptors.size || next.calls == 1) {
        "network interceptor $interceptor must call proceed() exactly once"
      }
    }

    check(response.body != null) { "interceptor $interceptor returned a response with no body" }

    return response
  }
1.4.4 Interceptor拦截器

        实现思路是通过Chain获取request或者response,然后对其进行重构再执行请求。

fun interface Interceptor {
  @Throws(IOException::class)
  fun intercept(chain: Chain): Response

  companion object {
    inline operator fun invoke(crossinline block: (chain: Chain) -> Response): Interceptor =
      Interceptor { block(it) }
  }
}
1.4.5 HeaderInterceptor
class HeaderInterceptor: Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        request = request.newBuilder() 
            .addHeader("key", "value") 
            .build()
        return chain.proceed(request)
    }
}
1.4.6 EncryptInterceptor
class EncryptInterceptor: Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        var request = chain.request()
        var charset = Charset.forName("UTF-8")
        val reqBody = request.body
        reqBody?.run {
            try {
                contentType()?.let {
                    charset = it.charset(charset)
                }
                val buffer = Buffer()
                writeTo(buffer)
                // 请求参数
                val reqData = URLDecoder.decode(buffer.readString(charset).trim(), "utf-8")
                val resData = 加密
                request = request.newBuilder()
                    .post(resData.trim().toRequestBody(contentType()))
                    .build()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return chain.proceed(request)
    }
}
1.4.7 DecryptInterceptor
class DecryptInterceptor: Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        var response = chain.proceed(request)
        val resBody = response.body
        resBody?.run {
            try {
                val source = source()
                source.request(Long.MAX_VALUE)
                val buffer = source.buffer
                var charset = Charset.forName("UTF-8")
                contentType()?.let {
                    charset = it.charset(charset)
                }
                val resData = buffer.clone().readString(charset)
                if (TextUtils.isEmpty(resData)) {
                    return response
                }
                // 解密
                val decryptStr = 解密
                response = response.newBuilder()
                    .body(decryptStr.toResponseBody(contentType()))
                    .build()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return response
    }
}

        OKhttp网络请求结果是由责任链执行返回的,而责任链是由拦截器列表组成,所以添加拦截器可以在请求过程中的进行统一处理。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值