美文网首页RxJavaAndroid知识程序员
Retrofit之请求发送、复用、取消

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

作者: DoAndKeep | 来源:发表于2016-08-23 14:19 被阅读7316次

    在之前,我们讨论了一些请求相关的内容,包括请求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. 欢迎大家对我进行批评教育。

    相关文章

      网友评论

        本文标题:Retrofit之请求发送、复用、取消

        本文链接:https://www.haomeiwen.com/subject/epfnsttx.html