gpt4 book ai didi

android - 使用 Retrofit Android 对多个 API 进行常见成功/失败/错误处理的良好设计

转载 作者:行者123 更新时间:2023-12-04 11:26:05 24 4
gpt4 key购买 nike

我想以这样一种方式设计 API 调用,以便从一个地方轻松处理成功和失败响应(而不是为所有 API 编写相同的调用函数代码)
以下是我想考虑的场景。

  • 在一个中心位置处理所有 API 的成功/失败和错误响应,例如 4xx、5xx 等。
  • 如果在注销的情况下已经发送了请求,想要取消入队请求并停止处理响应(因为响应解析会修改应用程序的一些全局数据)
  • 如果访问 token 已过期并且从云端收到 401 响应,它应该获取新 token ,然后使用新 token 自动再次调用 API。

  • 我目前的实现不满足上述要求。
    有没有办法使用 Retrofit 实现满足上述要求的 API 调用?
    请为此建议我一个好的设计。
    这是我目前的实现:
  • ApiInterface.java - 它是一个包含不同 API 调用定义的接口(interface)。
  • ApiClient.java - 获取改造客户端对象以调用 API。
  • ApiManager.java - 它具有调用 API 并解析其响应的方法。

  • 接口(interface)接口(interface).java
    public interface ApiInterface {

    // Get Devices
    @GET("https://example-base-url.com" + "/devices")
    Call<ResponseBody> getDevices(@Header("Authorization) String token);

    // Other APIs......
    }
    ApiClient.java
    public class ApiClient {

    private static Retrofit retrofitClient = null;

    static Retrofit getClient(Context context) {

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .sslSocketFactory(sslContext.getSocketFactory(), systemDefaultTrustManager())
    .connectTimeout(15, TimeUnit.SECONDS)
    .writeTimeout(15, TimeUnit.SECONDS)
    .readTimeout(15, TimeUnit.SECONDS)
    .build();

    retrofitClient = new Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .client(okHttpClient)
    .build();
    }
    }
    ApiManager.java
    public class ApiManager {

    private static ApiManager apiManager;

    public static ApiManager getInstance(Context context) {
    if (apiManager == null) {
    apiManager = new ApiManager(context);
    }
    return apiManager;
    }

    private ApiManager(Context context) {
    this.context = context;
    apiInterface = ApiClient.getClient(context).create(ApiInterface.class);
    }

    public void getDevices(ResponseListener listener) {
    // API call and response handling
    }
    // Other API implementation
    }
    更新:
    对于第一点,拦截器将有助于根据 this 在全局范围内处理 4xx、5xx 响应.
    但是拦截器将在 ApiClient 文件中并通知 UI 或 API 调用者组件,需要在回调中传递成功或失败结果,我的意思是响应监听器。
    我怎样才能做到这一点 ?任何想法 ?
    对于第三点,我对改造有点了解 Authenticator .我认为在这一点上它是合适的,但它需要同步调用才能使用刷新 token 获取新 token 。
    如何对 synchronous 进行异步调用? (注意:此调用不是改造调用)

    最佳答案

    通过在中心位置处理成功/失败响应,我假设您希望根据错误解析逻辑以及它如何为您的应用程序创建 UI 副作用来摆脱重复的样板文件。
    我可能会建议通过为 Callback 创建自定义抽象来使事情变得非常简单。它根据您的域逻辑调用您的 API 以获取成功/失败。
    这是用例 (1) 的一些相当简单的实现:

    abstract class CustomCallback<T> implements Callback<T> {

    abstract void onSuccess(T response);
    abstract void onFailure(Throwable throwable);

    @Override
    public void onResponse(Call<T> call, Response<T> response) {
    if (response.isSuccessful()) {
    onSuccess(response.body());
    } else {
    onFailure(new HttpException(response));
    }
    }

    @Override
    public void onFailure(Call<T> call, Throwable t) {
    onFailure(t);
    }
    }
    对于用例 (2),为了能够取消对全局事件(如注销)的所有排队调用,您必须保留对所有此类对象的引用。幸运的是, Retrofit支持插入自定义调用工厂 okhttp3.Call.Factory您可以将您的实现用作单例来保存调用集合,并在注销时通知它以取消所有正在进行的请求。请注意,请务必在集合中使用此类调用的弱引用,以避免泄漏/引用死调用。 (您也可能想针对要使用的正确集合进行头脑 Storm ,或者根据事务定期清理弱引用)
    对于用例 (3), Authenticator应该可以正常工作,因为您已经弄清楚了有两个选项的用法-
  • 将刷新 token 调用迁移到 OkHttp/Retrofit 并同步触发
  • 使用倒计时锁存器使身份验证器等待异步调用完成(将超时设置为刷新 token API 调用的连接/读/写超时)

  • 这是一个示例实现:
    abstract class NetworkAuthenticator implements Authenticator {

    private final SessionRepository sessionRepository;

    public NetworkAuthenticator(SessionRepository repository) {
    this.sessionRepository = repository;
    }

    public Request authenticate(@Nullable Route route, @NonNull Response response) {
    String latestToken = getLatestToken(response);

    // Refresh token failed, trigger a logout for the user
    if (latestToken == null) {
    logout();
    return null;
    }

    return response
    .request()
    .newBuilder()
    .header("AUTHORIZATION", latestToken)
    .build();
    }

    private synchronized String getLatestToken(Response response) {
    String currentToken = sessionRepository.getAccessToken();

    // For a signed out user latest token would be empty
    if (currentToken.isEmpty()) return null;

    // If other calls received a 401 and landed here, pass them through with updated token
    if (!getAuthToken(response.request()).equals(currentToken)) {
    return currentToken;
    } else {
    return refreshToken();
    }
    }

    private String getAuthToken(Request request) {
    return request.header("AUTHORIZATION");
    }

    @Nullable
    private String refreshToken() {
    String result = null;
    CountDownLatch countDownLatch = new CountDownLatch(1);

    // Make async call to fetch token and update result in the callback

    // Wait up to 10 seconds for the refresh token to succeed
    try {
    countDownLatch.await(10, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    return result;
    }

    abstract void logout();
    }
    我希望这对您的网络层实现有所帮助

    关于android - 使用 Retrofit Android 对多个 API 进行常见成功/失败/错误处理的良好设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67598849/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com