当前位置: 首页 > 知识库问答 >
问题:

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

微生景胜
2023-03-14

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

    null
  1. apiInterface.java-它是一个包含不同API调用定义的接口。
  2. apiclient.java-获取referfit客户机对象来调用API。
  3. apimanager.java--它有调用API并解析其响应的方法。

apiInterface.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
}

共有1个答案

祝花蜂
2023-03-14

通过在中心位置处理成功/失败响应,我假设您希望消除基于错误解析逻辑以及它如何为应用程序创建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),为了能够在logout这样的全局事件上取消所有排队调用,您必须保留对所有此类对象的引用。幸运的是,referfit支持插入自定义调用工厂OKHTTP3.call.factory

您可以将您的实现作为一个单例来保存调用集合,并在注销的情况下通知它取消飞行中的所有请求。请注意,一定要在集合中使用此类调用的弱引用,以避免泄漏/对死调用的引用。(另外,您可能希望针对要使用的正确集合或基于事务的弱引用定期清理进行集思广益)

对于用例(3),authenticator应该很好,因为您已经了解了用法,有两个选项-

  1. 将刷新令牌调用迁移到OKHTTP/regetfit并同步激发它
  2. 使用倒数锁存器使身份验证器等待异步调用完成(将超时设置为刷新令牌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();
}

我希望这有助于您的网络层实现

 类似资料:
  • 问题内容: 我正在使用具有以下功能的simplehtmldom: 我这样使用它: 有时,URL可能只是无效的,我想对此进行处理。我以为我可以使用try and catch,但是这没有用,因为它不会抛出异常,它只是给出了这样的php警告: 第39行在上面的代码中。 我如何正确处理此错误,我可以只使用普通条件,它看起来不像返回布尔值。 谢谢大家的帮助 更新资料 这是一个好的解决方案吗? 问题答案: 这

  • 问题内容: 我对处理Java中的异常有一些疑问。我读了一些,并得到了一些相互矛盾的指导方针。 异常处理的最佳实践 让我们看一下提到的文章: 它指出, 如果“客户端代码无法执行任何操作”, 通常应该避免使用检查的异常。但这到底是什么意思?在GUI中显示错误消息是否是冒充受检查的异常的充分原因?但这将迫使GUI程序员记住记住RuntimeException及其后代以显示潜在的错误信息。 本文提出的第二

  • Promise 链在错误(error)处理中十分强大。当一个 promise 被 reject 时,控制权将移交至最近的 rejection 处理程序(handler)。这在实际开发中非常方便。 例如,下面代码中所 fetch 的 URL 是错的(没有这个网站),.catch 对这个 error 进行了处理: fetch('https://no-such-server.blabla') // re

  • 问题内容: 使用错误返回码进行参数验证是否被视为良好做法?我的意思是有人应该在哪里使用错误而不是恐慌(有任何指导方针吗?)。 例如: 检查非nil +是否返回错误(如果为nil)是一个好习惯? 或检查正确的整数范围等。 我认为使用错误会使Go变得非常C-ish,并且看起来很糟糕。在这种情况下,恐慌是一个不错的选择吗? 还是Gopher应该使用Python / Ruby / JS方法“只是让它失败”

  • 问题内容: 我是新手,发现错误处理非常冗长。我已经读过它的理由并大体上同意,但是似乎在某些地方似乎有更多代码来处理错误而不是实际工作。这是一个(人为的)示例,我在其中传送“ Hello world!”。进入cat并读取并打印输出。基本上,每一行都可以再处理三个错误,而我什至没有处理任何事情。 有没有惯用的,干净的方法来处理此问题?我只是觉得我在想什么。 问题答案: 显然,我们必须处理任何错误。我们