Android Retrofit原理解析

一、引言

在 Android 开发中,网络请求是一项至关重要的功能。无论是获取新闻资讯、上传用户数据,还是与服务器进行实时交互,都离不开网络请求。一个高效、稳定且易于维护的网络请求解决方案对于开发者来说至关重要。

Retrofit 是 Square 公司开发的一款类型安全的 HTTP 客户端,它在 Android 和 Java 开发领域被广泛应用。Retrofit 以其简洁的 API 设计和强大的扩展性,极大地简化了网络请求的编写过程,让开发者能够专注于业务逻辑的实现。

本文将深入剖析 Retrofit 的原理,详细介绍其各个核心组件的工作机制,并结合具体的代码示例进行说明。通过阅读本文,你将对 Retrofit 有一个全面而深入的理解,从而能够更加熟练地使用它进行网络请求开发。

二、Retrofit 基础回顾

2.1 Retrofit 的依赖添加

在 Android 项目中使用 Retrofit,首先需要在 build.gradle 文件中添加相关依赖。以下是添加 Retrofit 及其常用转换器(如 Gson 转换器)的示例:

groovy 复制代码
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

com.squareup.retrofit2:retrofit 是 Retrofit 的核心库,提供了网络请求的基本功能。com.squareup.retrofit2:converter-gson 是 Gson 转换器库,用于将 JSON 数据转换为 Java 对象,反之亦然。

2.2 基本使用步骤

2.2.1 定义 API 接口

Retrofit 通过注解的方式将 HTTP 请求抽象成 Java 接口。以下是一个简单的示例:

java 复制代码
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface ApiService {
    @GET("users")
    Call<List<User>> getUsers(@Query("page") int page);
}

在这个示例中,@GET 注解指定了请求方法为 GET,"users" 是请求的相对 URL。@Query 注解用于添加查询参数,getUsers 方法返回一个 Call<List<User>> 对象,Call 表示一个可执行的网络请求,List<User> 是响应数据的类型。

2.2.2 创建 Retrofit 实例

使用 Retrofit.Builder 可以创建一个 Retrofit 实例,并进行相关配置:

java 复制代码
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {
    private static final String BASE_URL = "https://api.example.com/";
    private static Retrofit retrofit;

    public static Retrofit getRetrofitInstance() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                   .baseUrl(BASE_URL)
                   .addConverterFactory(GsonConverterFactory.create())
                   .build();
        }
        return retrofit;
    }
}

baseUrl 方法用于设置基础 URL,所有的请求 URL 都会基于这个基础 URL 进行拼接。addConverterFactory 方法用于添加数据转换器,这里使用了 Gson 转换器。

2.2.3 发起网络请求

获取 API 接口实例并发起网络请求:

java 复制代码
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity {
    public void fetchUsers() {
        Retrofit retrofit = RetrofitClient.getRetrofitInstance();
        ApiService apiService = retrofit.create(ApiService.class);
        Call<List<User>> call = apiService.getUsers(1);

        // 异步请求
        call.enqueue(new Callback<List<User>>() {
            @Override
            public void onResponse(Call<List<User>> call, Response<List<User>> response) {
                if (response.isSuccessful()) {
                    List<User> users = response.body();
                    // 处理响应数据
                } else {
                    // 处理请求失败的情况
                }
            }

            @Override
            public void onFailure(Call<List<User>> call, Throwable t) {
                // 处理网络请求失败的情况
            }
        });

        // 同步请求
        try {
            Response<List<User>> response = call.execute();
            if (response.isSuccessful()) {
                List<User> users = response.body();
                // 处理响应数据
            } else {
                // 处理请求失败的情况
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

retrofit.create(ApiService.class) 用于创建 API 接口的实例,enqueue 方法用于发起异步请求,execute 方法用于发起同步请求。

三、Retrofit 核心原理剖析

3.1 动态代理机制

3.1.1 Java 动态代理的基本概念

Java 动态代理是一种在运行时创建代理对象的机制。它允许在不修改目标对象的情况下,对目标对象的方法进行增强。动态代理主要通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口实现。

3.1.2 Retrofit 中动态代理的应用

当调用 retrofit.create(ApiService.class) 时,Retrofit 会使用动态代理为 ApiService 接口生成一个代理对象。以下是 retrofit.create() 方法的简化实现:

java 复制代码
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();

                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if (method.getDeclaringClass() == Object.class) {
                        return method.invoke(this, args);
                    }
                    if (platform.isDefaultMethod(method)) {
                        return platform.invokeDefaultMethod(method, service, proxy, args);
                    }
                    ServiceMethod<Object, Object> serviceMethod =
                            (ServiceMethod<Object, Object>) loadServiceMethod(method);
                    OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
                    return serviceMethod.callAdapter.adapt(okHttpCall);
                }
            });
}

Proxy.newProxyInstance 方法创建了一个代理对象,当调用 ApiService 接口的方法时,实际上会调用 InvocationHandlerinvoke 方法。在 invoke 方法中,会根据调用的方法创建 ServiceMethod 对象,然后创建 OkHttpCall 对象,最后通过 CallAdapter 进行适配并返回。

3.2 ServiceMethod 类

3.2.1 类的作用和功能概述

ServiceMethod 类用于封装请求的各种信息,包括请求方法、URL、参数、请求头、响应类型等。它是 Retrofit 中非常重要的一个类,负责将 API 接口的注解信息和方法参数转换为具体的 HTTP 请求信息。

3.2.2 内部成员变量及含义

以下是 ServiceMethod 类的部分成员变量:

java 复制代码
final class ServiceMethod<ResponseT, ReturnT> {
    private final okhttp3.Call.Factory callFactory;
    private final CallAdapter<ResponseT, ReturnT> callAdapter;
    private final Converter<ResponseBody, ResponseT> responseConverter;
    private final String httpMethod;
    private final String baseUrl;
    private final String relativeUrl;
    private final Headers headers;
    private final MediaType contentType;
    private final boolean hasBody;
    private final ParameterHandler<?>[] parameterHandlers;

    // 构造方法和其他方法...
}
  • callFactory:用于创建 OkHttp 的 Call 对象。
  • callAdapter:用于将 OkHttpCall 对象适配成我们需要的返回类型。
  • responseConverter:用于将响应数据转换为 Java 对象。
  • httpMethod:请求方法,如 GET、POST 等。
  • baseUrl:基础 URL。
  • relativeUrl:相对 URL。
  • headers:请求头。
  • contentType:请求体的内容类型。
  • hasBody:表示请求是否有请求体。
  • parameterHandlers:参数处理器数组,用于处理方法参数。

3.2.3 构建过程分析

ServiceMethod 的构建过程主要通过 Builder 类完成。以下是简化的构建过程:

java 复制代码
ServiceMethod(Builder<ResponseT, ReturnT> builder) {
    this.callFactory = builder.retrofit.callFactory();
    this.callAdapter = builder.callAdapter;
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.baseUrl = builder.retrofit.baseUrl().toString();
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.parameterHandlers = builder.parameterHandlers;
}

Builder 类会根据 API 接口的注解信息和方法参数,构建出请求的详细信息。例如,根据 @GET@POST 等注解确定请求方法,根据 @Query@Body 等注解处理方法参数。

3.3 OkHttpCall 类

3.3.1 与 OkHttp 的关系

Retrofit 底层使用 OkHttp 进行实际的网络请求。OkHttpCall 类是 Retrofit 基于 OkHttp 实现的一个具体的网络请求类,它封装了 OkHttp 的 Call 对象,并提供了统一的接口供 Retrofit 使用。

3.3.2 执行网络请求的流程

以下是 OkHttpCall 类的 enqueue 方法的简化实现:

java 复制代码
final class OkHttpCall<T> implements Call<T> {
    private final ServiceMethod<T, ?> serviceMethod;
    private final Object[] args;
    private volatile boolean canceled;
    private okhttp3.Call rawCall;

    OkHttpCall(ServiceMethod<T, ?> serviceMethod, Object[] args) {
        this.serviceMethod = serviceMethod;
        this.args = args;
    }

    @Override
    public synchronized void enqueue(final Callback<T> callback) {
        checkNotExecuted();
        boolean failFast = failureBehavior == FailureBehavior.FAIL_FAST;
        okhttp3.Call call;
        Throwable failure;

        synchronized (this) {
            if (canceled) {
                throw new IOException("Canceled");
            }
            try {
                call = rawCall = createRawCall();
            } catch (Throwable t) {
                failure = t;
                if (failFast) {
                    throw t;
                }
            }
        }

        if (failure != null) {
            callback.onFailure(this, failure);
            return;
        }

        if (canceled) {
            call.cancel();
        }

        call.enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                try {
                    callback.onFailure(OkHttpCall.this, e);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }

            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) throws IOException {
                Response<T> response;
                try {
                    response = parseResponse(rawResponse);
                } catch (Throwable e) {
                    if (failFast) {
                        throw e;
                    }
                    try {
                        callback.onFailure(OkHttpCall.this, e);
                    } catch (Throwable t) {
                        t.printStackTrace();
                    }
                    return;
                }

                try {
                    callback.onResponse(OkHttpCall.this, response);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
        });
    }

    private okhttp3.Call createRawCall() throws IOException {
        Request request = serviceMethod.toRequest(args);
        okhttp3.Call call = serviceMethod.callFactory.newCall(request);
        if (call == null) {
            throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
    }

    private Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();
        rawResponse = rawResponse.newBuilder()
               .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
               .build();

        int code = rawResponse.code();
        if (code < 200 || code >= 300) {
            try {
                ResponseBody bufferedBody = Utils.buffer(rawBody);
                return Response.error(bufferedBody, rawResponse);
            } finally {
                rawBody.close();
            }
        }

        if (code == 204 || code == 205) {
            rawBody.close();
            return Response.success(null, rawResponse);
        }

        ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
        try {
            T body = serviceMethod.responseConverter.convert(catchingBody);
            return Response.success(body, rawResponse);
        } catch (RuntimeException e) {
            catchingBody.throwIfCaught();
            throw e;
        }
    }

    // 其他方法...
}
  • enqueue 方法用于发起异步请求。首先会检查请求是否已经执行过,然后创建 OkHttp 的 Call 对象,最后调用 call.enqueue 方法执行异步请求。
  • createRawCall 方法根据 ServiceMethod 中的请求信息创建 OkHttp 的 Call 对象。
  • parseResponse 方法用于解析响应数据,将 okhttp3.Response 转换为 Response<T> 对象,并使用 responseConverter 将响应数据转换为 Java 对象。

3.4 CallAdapter 类

3.4.1 作用和功能

CallAdapter 用于将 OkHttpCall 对象转换为我们需要的返回类型。例如,在 API 接口中定义的 Call<List<User>>CallAdapter 会将 OkHttpCall 对象适配成 Call<List<User>> 对象。

3.4.2 默认的 CallAdapter 实现

Retrofit 默认提供了 DefaultCallAdapterFactory,它会将 OkHttpCall 对象适配成 Call 对象。以下是 DefaultCallAdapterFactory 的部分代码:

java 复制代码
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
            return null;
        }
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Object, Call<?>>() {
            @Override
            public Type responseType() {
                return responseType;
            }

            @Override
            public Call<Object> adapt(Call<Object> call) {
                return call;
            }
        };
    }
}

get 方法用于判断返回类型是否为 Call 类型,如果是则返回一个 CallAdapter 对象,adapt 方法直接返回传入的 Call 对象。

3.4.3 自定义 CallAdapter 的示例及使用场景

自定义 CallAdapter 可以实现一些特殊的功能,例如将返回类型转换为 RxJavaObservable 类型。以下是一个简单的自定义 CallAdapter 示例:

java 复制代码
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Retrofit;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

public class CustomCallAdapterFactory extends CallAdapter.Factory {
    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != CustomCall.class) {
            return null;
        }
        final Type responseType = Utils.getCallResponseType(returnType);
        return new CallAdapter<Object, CustomCall<?>>() {
            @Override
            public Type responseType() {
                return responseType;
            }

            @Override
            public CustomCall<Object> adapt(Call<Object> call) {
                return new CustomCall<>(call);
            }
        };
    }
}

class CustomCall<T> {
    private final Call<T> call;

    CustomCall(Call<T> call) {
        this.call = call;
    }

    // 自定义方法...
}

使用自定义 CallAdapter 时,需要在创建 Retrofit 实例时添加:

java 复制代码
Retrofit retrofit = new Retrofit.Builder()
       .baseUrl(BASE_URL)
       .addCallAdapterFactory(new CustomCallAdapterFactory())
       .addConverterFactory(GsonConverterFactory.create())
       .build();

3.5 Converter 类

3.5.1 数据转换的必要性

在网络请求中,服务器返回的数据通常是 JSON、XML 等格式,而我们在 Java 代码中需要使用 Java 对象来处理数据。因此,需要将服务器返回的数据转换为 Java 对象,反之亦然。Converter 类就是用于实现这种数据转换的。

3.5.2 常见的 Converter 实现(如 GsonConverterFactory)

GsonConverterFactory 是 Retrofit 中常用的转换器,它使用 Gson 来将 JSON 数据转换为 Java 对象。以下是 GsonConverterFactory 的使用示例和部分原理分析:

java 复制代码
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

// 创建 Retrofit 实例时添加 GsonConverterFactory
Retrofit retrofit = new Retrofit.Builder()
       .baseUrl(BASE_URL)
       .addConverterFactory(GsonConverterFactory.create())
       .build();

// GsonConverterFactory 的部分实现
public final class GsonConverterFactory extends Converter.Factory {
    private final Gson gson;

    public static GsonConverterFactory create() {
        return create(new Gson());
    }

    public static GsonConverterFactory create(Gson gson) {
        return new GsonConverterFactory(gson);
    }

    private GsonConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
    }

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

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonRequestBodyConverter<>(gson, adapter);
    }
}

// GsonResponseBodyConverter 用于将响应体转换为 Java 对象
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 {
        try {
            JsonReader jsonReader = gson.newJsonReader(value.charStream());
            return adapter.read(jsonReader);
        } finally {
            value.close();
        }
    }
}

// GsonRequestBodyConverter 用于将 Java 对象转换为请求体
final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
    private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
    private static final Charset UTF_8 = Charset.forName("UTF-8");

    private final Gson gson;
    private final TypeAdapter<T> adapter;

    GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public RequestBody convert(T value) throws IOException {
        Buffer buffer = new Buffer();
        Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
        JsonWriter jsonWriter = gson.newJsonWriter(writer);
        adapter.write(jsonWriter, value);
        jsonWriter.close();
        return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
    }
}

在上述代码中,GsonConverterFactory 实现了 Converter.Factory 接口,它的 responseBodyConverter 方法返回一个 GsonResponseBodyConverter 对象,用于将响应体转换为 Java 对象;requestBodyConverter 方法返回一个 GsonRequestBodyConverter 对象,用于将 Java 对象转换为请求体。

3.5.3 自定义 Converter 的实现步骤和示例代码

自定义 Converter 可以实现一些特殊的数据转换需求,例如处理 XML 数据。以下是一个简单的自定义 Converter 示例:

java 复制代码
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

// 自定义 ConverterFactory
public class CustomConverterFactory extends Converter.Factory {
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                           Retrofit retrofit) {
        if (type == String.class) {
            return new CustomResponseBodyConverter();
        }
        return null;
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                          Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        if (type == String.class) {
            return new CustomRequestBodyConverter();
        }
        return null;
    }
}

// 自定义响应体转换器
class CustomResponseBodyConverter implements Converter<ResponseBody, String> {
    @Override
    public String convert(ResponseBody value) throws IOException {
        return value.string();
    }
}

// 自定义请求体转换器
class CustomRequestBodyConverter implements Converter<String, RequestBody> {
    private static final MediaType MEDIA_TYPE = MediaType.get("text/plain; charset=UTF-8");

    @Override
    public RequestBody convert(String value) throws IOException {
        return RequestBody.create(MEDIA_TYPE, value);
    }
}

// 使用自定义 ConverterFactory
Retrofit retrofit = new Retrofit.Builder()
       .baseUrl(BASE_URL)
       .addConverterFactory(new CustomConverterFactory())
       .build();

在这个示例中,CustomConverterFactory 实现了 Converter.Factory 接口,根据类型判断是否使用自定义的转换器。CustomResponseBodyConverter 用于将响应体转换为字符串,CustomRequestBodyConverter 用于将字符串转换为请求体。

四、Retrofit 高级特性及原理关联

4.1 拦截器(Interceptor)

4.1.1 拦截器的作用和使用场景

拦截器是 Retrofit 中一个非常强大的特性,它可以在请求发送之前和响应返回之后对请求和响应进行拦截和处理。常见的使用场景包括添加请求头、日志记录、缓存处理等。

4.1.2 自定义拦截器的实现

以下是一个简单的自定义拦截器示例,用于添加请求头:

java 复制代码
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

public class HeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        Request newRequest = originalRequest.newBuilder()
               .header("Authorization", "Bearer your_token")
               .build();
        return chain.proceed(newRequest);
    }
}

在这个示例中,HeaderInterceptor 实现了 Interceptor 接口,在 intercept 方法中添加了一个请求头。

4.1.3 拦截器在 Retrofit 中的执行流程和原理关联

在创建 Retrofit 实例时,可以通过 OkHttpClient 添加拦截器:

java 复制代码
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

OkHttpClient client = new OkHttpClient.Builder()
       .addInterceptor(new HeaderInterceptor())
       .build();

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

当发起网络请求时,OkHttpCall 会调用 OkHttpClient 来执行请求,OkHttpClient 会按照添加的顺序依次执行拦截器的 intercept 方法。拦截器可以对请求进行修改,然后调用 chain.proceed 方法继续执行请求,最后返回响应。

4.2 缓存机制

4.2.1 缓存的重要性和作用

在网络请求中,缓存可以减少网络流量、提高响应速度,特别是在一些数据更新不频繁的场景下,缓存可以显著提升用户体验。

4.2.2 Retrofit 结合 OkHttp 实现缓存的原理

Retrofit 底层使用 OkHttp 进行网络请求,OkHttp 提供了强大的缓存机制。通过配置 OkHttpClient 的缓存策略,可以实现 Retrofit 的缓存功能。以下是一个简单的缓存配置示例:

java 复制代码
import okhttp3.Cache;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import java.io.File;

// 创建缓存目录
File cacheDirectory = new File(context.getCacheDir(), "http_cache");
int cacheSize = 10 * 1024 * 1024; // 10 MB
Cache cache = new Cache(cacheDirectory, cacheSize);

OkHttpClient client = new OkHttpClient.Builder()
       .cache(cache)
       .build();

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

在这个示例中,创建了一个 Cache 对象,并将其添加到 OkHttpClient 中。当发起网络请求时,OkHttp 会根据请求的缓存策略和缓存数据来决定是从缓存中获取数据还是重新发起网络请求。

4.2.3 缓存策略的配置和代码示例

可以通过添加拦截器来配置缓存策略。以下是一个简单的缓存拦截器示例:

java 复制代码
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

public class CacheInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        // 无网络时强制使用缓存
        if (!isNetworkAvailable()) {
            request = request.newBuilder()
                   .cacheControl(CacheControl.FORCE_CACHE)
                   .build();
        }
        Response response = chain.proceed(request);
        if (isNetworkAvailable()) {
            // 有网络时设置缓存时间
            int maxAge = 60; // 缓存 60 秒
            response = response.newBuilder()
                   .header("Cache-Control", "public, max-age=" + maxAge)
                   .removeHeader("Pragma")
                   .build();
        } else {
            // 无网络时设置缓存时间
            int maxStale = 60 * 60 * 24 * 28; // 缓存 28 天
            response = response.newBuilder()
                   .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                   .removeHeader("Pragma")
                   .build();
        }
        return response;
    }

    private boolean isNetworkAvailable() {
        // 检查网络连接状态
        return true;
    }
}

在这个示例中,CacheInterceptor 根据网络连接状态来配置缓存策略。有网络时,设置缓存时间为 60 秒;无网络时,强制使用缓存,并设置缓存时间为 28 天。

五、总结

Retrofit 通过动态代理机制将 API 接口的方法调用转换为具体的 HTTP 请求,使用 ServiceMethod 封装请求信息,OkHttpCall 执行网络请求,CallAdapter 进行返回类型的适配,Converter 进行数据转换。同时,Retrofit 还提供了拦截器和缓存等高级特性,方便开发者进行请求的处理和优化。

对于开发者来说,深入理解 Retrofit 的原理可以帮助我们更好地使用它进行网络请求开发。在实际开发中,我们可以根据项目的需求自定义 CallAdapterConverter 和拦截器等,以实现一些特殊的功能。同时,合理使用缓存和拦截器可以提高应用的性能和用户体验。

六、附录

6.1 完整的示例代码

以下是一个完整的 Retrofit 使用示例代码:

java 复制代码
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;
import retrofit2.http.Query;

import java.util.List;

// API 接口
interface ApiService {
    @GET("users")
    Call<List<User>> getUsers(@Query("page") int page);
}

// 用户类
class User {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

// Retrofit 客户端
class RetrofitClient {
    private static final String BASE_URL = "https://api.example.com/";
    private static Retrofit retrofit;

    public static Retrofit getRetrofitInstance() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                   .baseUrl(BASE_URL)
                   .addConverterFactory(GsonConverterFactory.create())
                   .build();
        }
        return retrofit;
    }
}

// 主活动
public class MainActivity {
    public void fetchUsers() {
        Retrofit retrofit = RetrofitClient.getRetrofitInstance();
        ApiService apiService = retrofit.create(ApiService.class);
        Call<List<User>> call = apiService.getUsers(1);

        call.enqueue(new Callback<List<User>>() {
            @Override
            public void onResponse(Call<List<User>> call, Response<List<User>> response) {
                if (response.isSuccessful()) {
                    List<User> users = response.body();
                    for (User user : users) {
                        System.out.println("Name: " + user.getName() + ", Age: " + user.getAge());
                    }
                } else {
                    System.out.println("Request failed: " + response.code());
                }
            }

            @Override
            public void onFailure(Call<List<User>> call, Throwable t) {
                System.out.println("Network error: " + t.getMessage());
            }
        });
    }

    public static void main(String[] args) {
        MainActivity activity = new MainActivity();
        activity.fetchUsers();
    }
}

6.2 相关参考资料和链接

通过阅读本文和参考相关资料,你可以更加深入地了解 Retrofit 的原理和使用方法,从而在 Android 开发中更加高效地处理网络请求。

相关推荐
扎瓦6 分钟前
Java 动态代理
java·后端·面试
独立开阀者_FwtCoder21 分钟前
# 一天 Star 破万的开源项目「GitHub 热点速览」
前端·javascript·面试
CatShitK31 分钟前
【Android】 如何将 APK 内置为系统应用(适用于编辑设置属性)
android·java·linux
天天扭码32 分钟前
前端进阶 | 面试必考—— JavaScript手写定时器
前端·javascript·面试
casual_clover35 分钟前
Android 中实现 GIF 图片动画
android
程序员Linc1 小时前
PP-OCR的安卓端部署
android·ocr·pp-ocr·安卓部署
{⌐■_■}2 小时前
【MySQL】索引运算与NULL值问题详解:索引字段应尽量 NOT NULL ,NULL值不能参与部分索引运算
android·数据库·mysql
笑川 孙2 小时前
为什么Makefile中的clean需要.PHONY
开发语言·c++·面试·makefile·make·技术
Goboy2 小时前
SQL面试实战,30分钟征服美女面试官
后端·面试·架构
天天扭码2 小时前
【硬核教程】从入门到入土!彻底吃透 JavaScript 中 this 关键字这一篇就够了
前端·javascript·面试