You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么Handler post执行逻辑中Toast消息会重复显示两次?

问题原因
  • 核心问题是你 post 到主线程的 Runnable 直接访问了跨线程共享的全局变量 progressStatus,没有做值快照。匿名内部类访问外部变量是引用传递,不是值拷贝,Runnable 被主线程执行时读取的是当前最新的 progressStatus,不是 post 时刻的值。
    举个实际执行场景:

工作线程把 progressStatus 加到 99 时,post 了一个更新进度的 Runnable,这个 Runnable 还没来得及在主线程执行,工作线程已经进入下一轮循环把 progressStatus 加到了 100,又 post 了第二个 Runnable。等主线程依次执行这两个 Runnable 时,读到的 progressStatus 都是 100,就会触发两次 toast 逻辑。

  • 次要原因是progressStatus没有做线程可见性保护,没有加volatile修饰,也没有同步锁,可能出现工作线程的修改没有及时同步到主线程,或者计数异常的问题。
  • 还有一种可能是你重复启动了这个工作线程,两次循环都跑到了100,各触发一次toast。
修复方案

方案1:post时拷贝当前进度值(最稳妥)

每次 post Runnable 前,把当前的进度存为 final 局部变量,Runnable 内部只访问这个局部快照,就不会读到后续更新的进度值:

new Thread(new Runnable() {
    @Override
    public void run() {
        while (progressStatus < 100) {
            progressStatus += 1;
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }       
            // 拷贝当前进度为final局部变量
            final int currentProgress = progressStatus;
            handler.post(new Runnable(){
                @Override
                public void run() {
                    pb = dialog.findViewById(R.id.no_connectiondProgressBar);
                    pb.setProgress(currentProgress);
                    // 用快照值判断,不是全局变量
                    if (currentProgress == 100) {
                        final Toast toast = new Toast(getApplicationContext());
                        toast.setDuration(Toast.LENGTH_LONG);
                        View custom_view = getLayoutInflater().inflate(R.layout.custom_toast, null);
                        toast.setView(custom_view);
                        toast.show(); 
                        dialog.dismiss();
                    }
                }
            });
        }
    }
}).start();

方案2:把toast触发逻辑移到工作线程

仅在进度到100的时候,才post显示toast的任务到主线程,避免每次都做判断,性能更好:

new Thread(new Runnable() {
    @Override
    public void run() {
        while (progressStatus < 100) {
            progressStatus += 1;
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }       
            final int currentProgress = progressStatus;
            handler.post(new Runnable(){
                @Override
                public void run() {
                    pb = dialog.findViewById(R.id.no_connectiondProgressBar);
                    pb.setProgress(currentProgress);
                }
            });
            // 工作线程直接判断,只post一次toast任务
            if (progressStatus == 100) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        final Toast toast = new Toast(getApplicationContext());
                        toast.setDuration(Toast.LENGTH_LONG);
                        View custom_view = getLayoutInflater().inflate(R.layout.custom_toast, null);
                        toast.setView(custom_view);
                        toast.show(); 
                        dialog.dismiss();
                    }
                });
            }
        }
    }
}).start();

额外优化建议

给全局变量 progressStatus 加上 volatile 修饰,保证多线程之间的可见性,避免出现工作线程的进度更新主线程读取不到的问题:

private volatile int progressStatus = 0;

内容的提问来源于stack exchange,提问作者Jin Appsdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 22:06:03