Retrofit之请求发送、复用、取消

在之前,我们讨论了一些请求相关的内容,包括请求Url、请求参数以及请求头,如果没有阅读的建议了解下,以免影响本文的阅读。本文讨论请求的发送、取消及复用等内容,代码基于《Retrofit之初体验》

请求发送

请求发送分为两部分:同步请求和异步请求。在Retrofit中,每个请求都被包装成一个Call对象,发送同步请求还是异步请求都是在Call对象上做文章,接下来就分开讨论。以请求微博广场微博API为例,接口如下:

@GET("statuses/public_timeline.json")
Call<Timeline> timelineForPublic(@Query("count") int count, @Query("page") int page);

如果您看不懂这个方法,建议你可以从我的Retrofit文集开始阅读。

同步请求

当我们获取到Call对象时,可以通过使用call.execute()来执行一个同步请求。对应的代码如下:

WeiboService weiboService = ServiceGenerator.createService(WeiboService.class);
Call<Timeline> call = weiboService.timelineForPublic(COUNT_PER_REQ, page);
Timeline timeline = call.execute().body();

首先,使用ServiceGenerator创建了WeiboService的一个实例,之后初始化执行请求的call对象,最后执行call接收到Timeline响应。

当然,如果在Android4.0或之上的UI线程中执行,则会抛出NetworkOnMainThreadException错误。因为同步请求会阻塞线程,但因其只阻塞它启动的那个线程,因此我们可以在一个异步线程中来执行同步请求。

异步请求

使用call.enqueue()方法执行异步请求,Retrofit会处理其在另一线程执行,从而不会阻塞UI。

执行异步请求需要实现一个Callback,它有两个回调方法onResponse()和onFailure(),回调实现定义了当请求结束时需要做什么,示例如下:

WeiboService weiboService = ServiceGenerator.createService(WeiboService.class);
Call<Timeline> call = weiboService.timelineForPublic(COUNT_PER_REQ, page);
call.enqueue(new Callback<Timeline>() {
    @Override
    public void onResponse(Call<Timeline> call, Response<Timeline> response) {
        if (response.isSuccessful()) {
            mAdapter.addData(response.body().getStatuses());
        } else {
            DebugLog.i(TAG, "request is failed");
        }
    }

    @Override
    public void onFailure(Call<Timeline> call, Throwable t) {
        DebugLog.i(TAG, "error:" + t.getMessage());
    }
});

只要响应可以正确处理,即使状态码不在200-299范围内,也会回调onResponse()方法。Response类有一个便捷方法isSuccessful()来检查请求是否处理成功(返回状态码2XX),如果成功就可以使用响应对象做进一步处理。如果状态码不是2XX,你需要自己处理错误。

我们将微博的token值更改下,进行模拟请求失败的情况。此时,返回的状态码为400,onResponse()得到回调,并进入到了else分支,打印了”request is failed”。此时,响应主体内容如下:

{"error":"source paramter(appkey) is missing","error_code":10006,"request":"/2/statuses/public_timeline.json"}

可以根据具体的响应内容进一步处理,这里只单独打印出请求错误。

同时,在这里我们可以看到每个回到方法中都包含call对象,这个是用来分析请求的。我们通过call.request()可以获取到Request对象,之后就可以复查请求了:

Headers requestHeaders = request.headers(); 
RequestBody requestBody = request.body(); 
HttpUrl requestUrl = request.url();

需要注意的是:如果请求尚没有执行,不要在UI线程中调用call.request(),因为这个方法会进行大量的计算,属于耗时操作。

请求复用

首先要明确一点:每个Call实例可以且只能执行一次请求,不能使用相同的对象再次执行execute()或enqueue()。

但是,很多时候我们都需要再次执行请求,例如初次请求超时失败,这时候就要刷新再次请求。如果我们需要再次执行请求,可以使用call.clone()方法来创建call的一个副本,这个副本与call包含相同的配置,我们可以使用这个副本来再次执行相同的请求。代码示例如下:

WeiboService weiboService = ServiceGenerator.createService(WeiboService.class);
Call<Timeline> call = weiboService.timelineForPublic(COUNT_PER_REQ, page);
call.enqueue(callback);

Call<Timeline> newCall = call.clone();
newCall.enqueue(callback);

取消请求

很多时候,当用户离开了当前页面,该页面中尚未完成的请求就没有必要了,这时候我们就需要取消请求了。在Retrofit中实现非常简单,使用call.cancel()来取消请求。

取消很简单,但是我们也应该处理取消之后的后续操作。当一个请求取消时,回调方法onFailure()会执行,而onFailure()方法在没有网络或网络错误的时候也会执行。这两种情况下我们的处理是不一样的,如果没有网络,我们会告诉用户连接网络,而取消请求则有可能只取消进度条。因此,需要对这两种情况进行区分,代码如下:

@Override
public void onFailure(Call<Timeline> call, Throwable t) {
    if (call.isCanceled()) {
        DebugLog.i(TAG, "request is canceled");
    } else {
        DebugLog.i(TAG, "error:" + t.getMessage());
    }
}

通过call.isCanceled()方法来判断是否请求取消,从而对不同的情况分别处理。

至此,关于请求这块就讨论到这里,下次将讨论Retrofit中的响应处理。

源码地址:
https://github.com/FILWAndroid/DevJourney

关于源码:

  1. 不只是本文涉及的代码,会包含很多知识点的代码,应该都会在我的简书中进行介绍。
  2. 有可能代码与本文中所贴出来的有差异,但应该都是我觉得更好的方式吧。
  3. 新浪微博相关的代码运行显示不出来结果,感兴趣的可以参考新浪微博SDK,配置工程。
  4. 欢迎大家对我进行批评教育。
    原文作者:DoAndKeep
    原文地址: https://www.jianshu.com/p/206f0060e1e4
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞