子线程中创建 handler导致okhttp请求失败,从 ScheduledExecutorService 挖的坑开始

子线程创建方法1:

                ScheduledExecutorService schedulePool = Executors.newScheduledThreadPool(2);
                schedulePool.schedule(new Runnable() {
                    @Override
                    public void run() {
                        dorequest();
                    }
                }, 2, TimeUnit.SECONDS);

子线程创建方法2:

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        dorequest();
                    }
                }).start();

dorequest 代码: 

   Log.e("aaaaa", "dorequest");

        Request request = new Request.Builder() .url("http://www.baidu.com").build();
        okhttp3.OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();

            
        // 导致请求停止的原因
        Handler h = new Handler();

        clientBuilder.build().newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e("aaaaa", "onFailure");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e("aaaaa", "onResponse");

            }
        });


创建方法1 的运行结果:

方法运行中断,没有结果 。中断直接连网络请求都没有发出

创建方法2的运行结果:

直接crash,没有触发Looper.prepare()


问题的原因是在子线程中直接 new 了 handler 。如果不带 handler 时则正常。

由此有两种解决方案:

 方案一:补全 looper

private void dorequest(){
        Log.e("aaaaa", "dorequest");

        Request request = new Request.Builder() .url("http://www.baidu.com").build();
        okhttp3.OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();

        // 在new之前触发 prepare
        Looper.prepare();
        Handler h = new Handler();

        clientBuilder.build().newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.e("aaaaa", "onFailure");
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e("aaaaa", "onResponse");

            }
        });

        // 在完成时要 loop
        Looper.loop();
    }

方案二:指定主线程中创建 handler

// 修改这一句即可
Handler h = new Handler(Looper.getMainLooper());


这次写这个不是因为 looper,而是因为 schedulePool 在没有处理 looper 时居然不报错,而是直接return了,正好碰到这个问题,发现 okhttp 没有实际请求,查半天才发现是这个漏处理了,所以记录一下。

ScheduledExecutorService

特别需要注意,一旦SES执行的逻辑中抛出异常,那么调度会自动停止,并且不会有任何提示信息。在其scheduleWithFixedDelay()和scheduleAtFixedRate()方法的JavaDoc中,都有这样的描述:

ScheduledExecutorService (Java Platform SE 8 )

If any execution of the task encounters an exception, subsequent executions are suppressed.

Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given delay between the termination of one execution and the commencement of the next. If any execution of the task encounters an exception, subsequent executions are suppressed. Otherwise, the task will only terminate via cancellation or termination of the executor.

同时,通过线程工厂ThreadFactory设置异常处理器UncaughtExceptionHandler是没有作用的,也就是说SES遇到异常时根本不会调用uncaughtException()方法。

具体错误捕获,请参见:

ScheduledExecutorService异常处理的正确姿势-CSDN博客

Java中的定时任务-ScheduledExecutorService的坑 | yilan 

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值