实施Retrofit2服务接口

时间:2016-06-02 15:20:50

标签: android retrofit retrofit2 android-networking

想象一下以下情况。

我正在使用Retrofit作为我的API客户端,并且我将使用缓存机制在本地持有一些响应,例如SQLite数据库,因此一些数据只获得一次。对于以下接口创建自定义实现似乎是一个完美的案例,当网络不可用时,该接口将从本地数据库获取数据。

public interface CommentsService {  
    @GET("posts/{postId}/comments")
    Call<List<Comment>> getCommentsOfPost(@Path("postId") int id);
}

问题是retrofit2中的所有服务方法都应该包装在Call对象中,因为它会阻止将回调作为参数传递,就像在以前版本的改进中一样。

我想知道创建此接口的自定义实现是否是一个好习惯,如果是,那么如何处理Call对象?

6 个答案:

答案 0 :(得分:4)

在我的一个项目中,我遇到了类似的问题,无法使用RxJava。我通过使用命令模式解决了这个问题。

首先,我创建了一个带有明确API的自定义回调类。这样我就可以更简洁地处理HTTP代码(例如400或500)。

public abstract class RestCallback<T> implements Callback<T> {

    @Override
    public void onResponse(Response<T> response, Retrofit retrofit) {
        if (response != null) {
            if (response.isSuccess()) {
                onSuccess(response, retrofit);
            } else {
                onError(response, retrofit);
            }
        } else {
            onFailure(null);
        }
    }

    abstract public void onSuccess(Response<T> response, Retrofit retrofit);

    abstract public void onError(Response<T> response, Retrofit retrofit);

    abstract public void onFailure(Throwable t);

}

然后我创建了一个 Command 界面:

public interface Command {

    // New network hit
    void fresh(RestCallback callback);

    // First cached if available then fresh
    void all(RestCallback callback);

    // Cached response only
    void cached(RestCallback callback);

    // If cache exists return it, otherwise return a fresh response
    void get(RestCallback callback);

    // Cancel the request
    void cancel();

}

这样,您将实现Command接口并使用它,而不是直接使用Retrofit对象。例如,您可以创建一个GetCommentsOfPostCommand,让它处理Retrofit Call对象,封装请求逻辑。

Android Retrofit Command Pattern

这样你就可以随心所欲地工作;在暴露定义明确的界面的同时实现缓存,请求,取消等。

希望这有帮助。

答案 1 :(得分:1)

如果我正确理解您的问题,您就可以从SQLite中获取数据,以防万一没有网络连接。

您有此界面,您可以在其中定义端点:

public interface CommentsService {  
    @GET("posts/{postId}/comments")
    Call<List<Comment>> getCommentsOfPost(@Path("postId") int id);
}

然后你应该有另一个界面,你可以从你的应用程序的另一部分使用它:

public interface GetCommentsService {
    void getComments(int id, CommentsServiceResultListener listener);
}

其中CommentsServiceResultListener是您传递给上一个接口实现的侦听器。我们将其定义如下:

public interface CommentsServiceResultListener {

    void onResponse(List<Comment> response);

    void onError(String errorMessage);
}

您需要实现GetCommensService接口,以便实际获取数据。你可以这样做:

public class GetCommensServiceImpl implemens GetCommensService {
    private static final String TAG = BuildingsBaseServiceImpl.class.getSimpleName();

    @Override
    public void getComments(int id, CommentsServiceResultListener listener) {
        CommentsService service = getService();
        Call<List<Comment>> request = service.getCommentsOfPost(id);
        request.enqueue(new Callback<List<Comment>>(){

            @Override  //if this method is executed, the actual call has been made
            public void onResponse(Call<List<Comment>> call, Response<List<Comment>> response) { 
                if (response.isSuccessful()) {
                    listener.onResponse(response.body());
                } else {
                    //TODO check here if the call wans't successful because a network problem. In that case, fetch from your SQLite
                    //Default unsuccessful call management
                    Log.e(TAG, response.code() + ": " + response.message());
                    listener.onError(response.message());
                }
            }

            @Override //maybe the call couldn't be made because of lack of connection. 
            public void onFailure(Call<List<Comment>> call, Throwable t) {
                //TODO check the failure cause, then decide if there's need to fetch from your SQLite.
                //Default failure management
                Log.e(TAG, t.getMessage() + "", t);
                listener.onError(t.getMessage() + "");
            }
        });
    }

    private CommentsService getService() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://your.base.url/")
                .addConverterFactory(GsonConverterFactory.create()) //assuming JSON
                .build();
        return retrofit.create(CommentsService.class);
    }
}

希望这会有所帮助!!

答案 2 :(得分:0)

在retrofit2中,您可以使用Call.enqueue(Callback)指定回调。例如,

getCommentsService.getCommentsOfPost(myInt).enqueue(new Callback() {
    @Override
    public void onResponse(Call call, Response response) {
        // do something with the response
    }
}

如果您想要同步执行,请使用Call.execute()

答案 3 :(得分:0)

如果您有使用RxJava的经验,它会变得更容易,因为您可以在获取数据时组合多个任务,并尽快显示它。因此,您首先要做的是将api结果保存到数据库中。下次要显示数据时,可以调用concat方法连接多个源请求(从缓存,磁盘或服务器)以尽快检索数据。

以下是对此的良好解释。

http://blog.danlew.net/2015/06/22/loading-data-from-multiple-sources-with-rxjava/ https://medium.com/@murki/chaining-multiple-sources-with-rxjava-20eb6850e5d9#.2u8zssice

答案 4 :(得分:0)

由于您只想在网络不可用时检查本地数据库,您还可以实现一个回调接口并将其作为参数传递给正在进行改装调用的函数。这样,您可以使您的函数返回void,并且从您的改装调用的onResonse或onFailure方法内部,您可以决定是从成功响应(onResponse)还是从本地数据库(onFailure)加载,即,创建所需的调用自定义界面所需的功能。这将模仿您在异步调用中改装调用的同步行为,同时即使您收到不正确的响应,也可以灵活地做出决策。

答案 5 :(得分:0)

好吧,让我们想象一下,Retrofit也提供了机制(例如,如果错误 - 从DB加载,则传递特殊回调)。这将是非常具体的,因为:

  • 如果我想更新令牌,请提供安全数据
  • 如果我想手动重新检查连接
  • 如果我想要显示特殊数据
  • ...

使用Retrofit的主要任务是获取连接结果和工作状态。这就是全部。

也回答你的问题。对于没有网络连接的情况,您可以提供自己的行为,并调用onDataBaseLoadIfError()。而不是打电话给你重写的改造班 - 打电话。

public interface CommentsServiceListener {

    void onResponse(List<Comment> response);

    void onError(String errorMessage);

    void onDataBaseLoadIfError(String errorMessage);
}