Toast弹不出来之谜

前言

今天早上测试应用的时候,忽然发现Toast弹不出来了,我用的华为测试机,以为是通知权限被关了,后来发现是开着的,这就纳了闷了,这个Toast工具类用了好长时间了,后来发现这Toast原来还能这样...

正文

以前工具类是这个样子的(华为的通知权限放在了其他的地方)

final public class ToastUtil {
    private static Toast toast;//单例的toast

    /**
     * 显示Toast
     */
    public static void showToast(int text) {
        showToast(App.getInstance().getResources().getString(text));
    }

    public static void showToast(String text) {
        if (toast == null)
            toast = Toast.makeText(App.getInstance(), text, Toast.LENGTH_SHORT);
        toast.setText(text);
        toast.show();
    }
}

平时用的都是好好的,为什么不能用了呢?

然后我通过debug发现所有路径都走了,说明是调用成功了,后来想起来,是不是子线程的问题,打印了一下线程信息:

Thread.currentThread().getName();

发现果然第一次调用没有跑在主线程中,这就尴尬了,在子线程中创建,并show()竟然没有报错,而且子线程创建的在主线程调用show()也没有报错,只是不显示

到show()的源码中发现,被try了..

    /**
     * Show the view for the specified duration.
     */
    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        TN tn = mTN;
        tn.mNextView = mNextView;

        try {
            service.enqueueToast(pkg, tn, mDuration);
        } catch (RemoteException e) {
            // Empty
        }
    }

修改方法

在创建和show的时候可以判断是否是主线程,如果不是就放到主线程中就ok了

修改后的代码,嗯,这样就可以了(虽然有强迫症看到这代码很不爽,但是首先得能用)

final public class ToastUtil {
    private static Toast toast;//单例的toast

    /**
     * 显示Toast
     */
    public static void showToast(int text) {
        showToast(App.getInstance().getResources().getString(text));
    }

    public static void showToast(final String text) {
        if (toast == null) {
                //AppManager.getAppManager().currentActivity()是使用工具类获取当前Activity对象的方法
                AppManager.getAppManager().currentActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        toast = Toast.makeText(App.getInstance(), text, Toast.LENGTH_SHORT);
                        toast.show();
                    }
                });
        } else {
            //如果show()是在子线程触发的,则在主线程来显示
            if ("main".equals(Thread.currentThread().getName())) {
                toast.setText(text);//将文本设置给toast
                toast.show();
            } else {
                AppManager.getAppManager().currentActivity().runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        toast.setText(text);//将文本设置给toast
                        toast.show();
                    }
                });
            }
        }
    }
}

ps:近些天看到了一个类似的情况,比我分析的源码更多:怀疑人生,主线程修改UI也会崩溃?

扩展

如果你因为某些情况,真的要在某个(注意是某个)子线程中弹出toast,可以使用以下方法在子线程开一个loop,这样就可以弹toast了(当然你要自己知道后果,具体百度):

    thread {
        Looper.prepare()
        Toast.makeText(context, "123", Toast.LENGTH_LONG).show()
        Looper.loop()
    }

如果遇到了连续弹toast后,导致后续一段时间弹不出toast,可以使用以下方法 ,效果看起来一样,只不过每次调用都是使用的新toast对象:

//将这两行代码
                toast.setText(text);//将文本设置给toast
                toast.show();
改为:
                    toast.cancel()
                    toast = Toast.makeText(app, text, Toast.LENGTH_LONG)
                    toast.show()

end

对Kotlin或KMP感兴趣的同学可以进Q群 101786950

如果这篇文章对您有帮助的话

可以扫码请我喝瓶饮料或咖啡(如果对什么比较感兴趣可以在备注里写出来)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值