当前位置:首页 > 编程笔记 > 正文
已解决

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

来自网友在路上 155855提问 提问时间:2023-10-22 13:52:24阅读次数: 55

最佳答案 问答题库558位专家为你答疑解惑

一、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在扩展性方面是做的非常好的。

constructor() : this(Builder())init {if (connectionSpecs.none { it.isTls }) {this.sslSocketFactoryOrNull = nullthis.certificateChainCleaner = nullthis.x509TrustManager = nullthis.certificatePinner = CertificatePinner.DEFAULT} else if (builder.sslSocketFactoryOrNull != null) {this.sslSocketFactoryOrNull = builder.sslSocketFactoryOrNullthis.certificateChainCleaner = builder.certificateChainCleaner!!this.x509TrustManager = builder.x509TrustManagerOrNull!!this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(certificateChainCleaner!!)} else {this.x509TrustManager = Platform.get().platformTrustManager()this.sslSocketFactoryOrNull = Platform.get().newSslSocketFactory(x509TrustManager!!)this.certificateChainCleaner = CertificateChainCleaner.get(x509TrustManager!!)this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(certificateChainCleaner!!)}verifyClientState()
}

        以上是OkHttpClient的构造方法的初始化方法,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> {@NullableT 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:

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • JAXB: com.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 uncheckedreturn (Converter<T, RequestBody>) converter;}}......}

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

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];@Overridepublic @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;}

         通过代码可知创建okhttp请求是由ServiceMethod类执行,接着再看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。

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,同时HttpServiceMethod继承于ServiceMethod,那么invoke就在这里实现了,那么剩余retrofit其他操作基本都可以在这里找到了,毕竟Service代理执行完invoke就结束了。

HttpServiceMethod(RequestFactory requestFactory,okhttp3.Call.Factory callFactory,Converter<ResponseBody, ResponseT> responseConverter) {this.requestFactory = requestFactory;this.callFactory = callFactory;this.responseConverter = responseConverter;}......@Overridefinal @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;}@Overrideprotected 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.interceptorsinterceptors += RetryAndFollowUpInterceptor(client)interceptors += BridgeInterceptor(client.cookieJar)interceptors += CacheInterceptor(client.cache)interceptors += ConnectInterceptorif (!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 = falsetry {val response = chain.proceed(originalRequest)if (isCanceled()) {response.closeQuietly()throw IOException("Canceled")}return response} catch (e: IOException) {calledNoMoreExchanges = truethrow 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 拦截器接口类

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

fun interface Interceptor {@Throws(IOException::class)fun intercept(chain: Chain): Responsecompanion object {inline operator fun invoke(crossinline block: (chain: Chain) -> Response): Interceptor =Interceptor { block(it) }}
}
1.4.5 Header拦截器实现类
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 加密拦截器实现类
class EncryptInterceptor: Interceptor {override fun intercept(chain: Interceptor.Chain): Response {var request = chain.request()var charset = Charset.forName("UTF-8")val reqBody = request.bodyreqBody?.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 解密拦截器实现类 
class DecryptInterceptor: Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()var response = chain.proceed(request)val resBody = response.bodyresBody?.run {try {val source = source()source.request(Long.MAX_VALUE)val buffer = source.buffervar 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网络请求结果是由责任链执行返回的,而责任链是由拦截器列表组成,所以添加拦截器可以在请求过程中的进行统一处理。 

查看全文

99%的人还看了

猜你感兴趣

版权申明

本文"常用网络请求框架Retrofit学习笔记":http://eshow365.cn/6-21669-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!