如何取消所有排队的请求onFailure? (改造 2)

Posted

技术标签:

【中文标题】如何取消所有排队的请求onFailure? (改造 2)【英文标题】:How to cancel all queued request onFailure? (Retrofit2) 【发布时间】:2019-05-15 19:15:22 【问题描述】:

实际上我在我的应用程序中使用 Retrofit2 向我的服务器发送一些文本。

问题是当我在网络中断时使用射频设备时,我正在 onFailure 呼叫应该被取消,但是一旦设备重新连接到网络,它似乎正在重新发送文本或类似的东西该文本仍在队列中,我会处理它。

这是我发送文本的方法(我还尝试在 onFailure 中使用 call.cancel() 但没有效果)

  @SuppressLint("DefaultLocale")
    public void sendPost() 
        @SuppressLint("SdCardPath", "DefaultLocale") final File file = new File("/data/data/app/files/"+String.format("%03d", Integer.valueOf(nTerminalino))+"_"+nTavoli.getText().toString()+".txt");

        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);

        RequestBody fbody = RequestBody.create(MediaType.parse("text/*"), file);
        builder.addFormDataPart(String.format("%03d", Integer.valueOf(nTerminalino))+"_"+nTavoli.getText().toString(), file.getName(),fbody);

        MultipartBody requestBody = builder.build();

        final APIService apiService = ApiUtils.getAPIService(ipCASSA);

        final Call<Void> calls = apiService.savePost(requestBody);

        calls.enqueue(new Callback<Void>() 
            @Override
            public void onResponse(@NonNull Call<Void> call, @NonNull Response<Void> response) 
                if (response.isSuccessful()) 
                    Log.i("RESPONSE: ", response.toString());
                    Print();
                    file.delete();
                    dialogLoading.dismiss();
                    Runtime.getRuntime().gc();
                else 
                    calls.cancel();
                
            

            @Override
            public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) 
                    Log.e("TAG", t.toString());
                    MediaPlayer mpFound = MediaPlayer.create(pterm.this, R.raw.errorsound);
                    mpFound.start();
                    Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                    if (v.hasVibrator()) 
                        v.vibrate(6000);
                    
                    new GlideToast.makeToast(pterm.this, "CONNESSIONE FALLITA!", GlideToast.LENGTHLONG, GlideToast.FAILTOAST).show();
                    dialogLoading.dismiss();

            
        );
    

在我正在构建 OkHttpClient 的班级中,我设置了 retryOnConnectionFailure(false) 但仍然无法解决即使 onFailure 我也收到 POST 请求的问题。

更新:

经过一番研究,我发现 OkHttp 会在连接失败时静默重试 POST。

那么如何防止客户端重试呢?

一些信息:

我的应用程序是服务员的应用程序,所以当我收到 onFailure 消息时,我通知服务员收据尚未被 POS 接收且尚未打印,但 OkHttpClient 仍然重试发送文件,我强迫服务员在onFailure 之后重新发送收据,这会导致打印重复的收据。

这是我的 ApiUtils 类、APIService 接口和 RetrofitClient 类

class ApiUtils 

    private ApiUtils() 



    static APIService getAPIService(String ipCASSA) 

        String BASE_URL = "http://"+ipCASSA+"/web/";

        final OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .connectTimeout(10000, TimeUnit.MILLISECONDS)
                .writeTimeout(10000, TimeUnit.MILLISECONDS)
                .readTimeout(10000, TimeUnit.MILLISECONDS)
                .retryOnConnectionFailure(false)
                .build();


        return RetrofitClient.getClient(BASE_URL,okHttpClient).create(APIService.class);
    


public interface APIService 

    @POST("CART=PTERM")
    Call<Void> savePost(@Body RequestBody text);



class RetrofitClient 

    private static Retrofit retrofit = null;


    static Retrofit getClient(String baseUrl, OkHttpClient okHttpClient) 
        if (retrofit==null) 
            retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(okHttpClient)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        
        return retrofit;
    

【问题讨论】:

究竟是什么行为让您认为请求被发送了两次?你只在你的服务器上看到它吗?您是否看到onFailure,当网络重新连接时您是否看到onSuccess @JanosBreuer 实际上我得到了类似于此处medium.com/inloopx/… 在“解决真正的问题”点中描述的内容我正在发送网络中断的文件,所以我没有得到响应,但是文件已发送 【参考方案1】:

您可以尝试直接取消通话。因此,当调用失败时,在onFailure 回调中,您可以执行类似的操作

@Override
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) 
    if (!call.isCanceled()) 
        // Do receipt logic
        call.cancel();
    

【讨论】:

【参考方案2】:

由于retryOnConnectionFailure(false)没有帮助,所以问题不是OKHttp重试造成的。

我怀疑您在复制请求。检查sendPost()是否被多次调用。

【讨论】:

您确定 retryOnConnectionFailure 会有所帮助吗?正如我读过这篇文章medium.com/inloopx/… 所说的那样,okhttp 正在悄悄地重试网络错误 实际上我已经检查了代码,当我点击“发送”按钮时,我只调用了一次 sendPost()... 尝试在sendPost() 中添加日志。并检查它是否不止一次发生。

以上是关于如何取消所有排队的请求onFailure? (改造 2)的主要内容,如果未能解决你的问题,请参考以下文章

改造。如果 (!response.isSucessful()) 得到响应

如何在 Angular 4+ 中取消/取消订阅所有挂起的 HTTP 请求

改造获取多个请求的最后一个请求

设计模式-行为型-命令模式(COMMAND)

Retrofit-非空主体上的onFailure,空主体上的onResponse

自定义呼叫适配器改造中的通用类型