Toast不是乱弹的

一  前言

Toast在android里面弹的很多了,但是在分线程里面弹会怎么样呢? 有些同学会说,那还不是和平时一样的弹?!其实,也不尽然,不信?请继续往下面看。


二 分线程弹Toast引发的问题

如果在分线程里面直接弹Toast,比如下面这样,button点击的时候启动一个线程,弹出一个Toast:


那么就会得到一个应用程序异常终止的错误弹窗。

原因如下:
复制内容到剪贴板
代码:
E/AndroidRuntime( 8978): FATAL EXCEPTION: Thread-2221

E/AndroidRuntime( 8978): Process: com.example.androidtest, PID: 8978

E/AndroidRuntime( 8978): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

E/AndroidRuntime( 8978):         at android.os.Handler.<init>(Handler.java:200)

E/AndroidRuntime( 8978):         at android.os.Handler.<init>(Handler.java:114)

E/AndroidRuntime( 8978):         at android.widget.Toast$TN.<init>(Toast.java:347)

E/AndroidRuntime( 8978):         at android.widget.Toast.<init>(Toast.java:100)

E/AndroidRuntime( 8978):         at android.widget.Toast.makeText(Toast.java:254)

E/AndroidRuntime( 8978):         at com.example.androidtest.MainActivity$1$1.run(MainActivity.java:40)

E/AndroidRuntime( 8978):         at java.lang.Thread.run(Thread.java:818)

D/ActivityManager(  939): addErrorToDropBox processName = com.example.androidtest
意思是如果没有调用Looper.prepare(),就不能初始化Handler对象。看错误堆栈,是Toast.makeText()的时候会初始化Handler对象。

这个调用过程如下:

1. 从Toast.makeText(...)开始
1 public static Toast makeText(Context context, CharSequence text, int duration) {
2         Toast result = new Toast(context);
3  
4         ...
5  
6         return result;
7     }
它会初始化一个Toast对象,继续看Toast的构造函数:

2.
1 public Toast(Context context) {
2         mContext = context;
3         mTN = new TN();
4         ...
5     }
在Toast的构造函数里面,会初始化一个TN对象,这个TN对象是什么东西呢?

3.
1 private static class TN extends ITransientNotification.Stub {
2 final Handler mHandler = new Handler();   
3 ...
4 }
这个TN对象是Binder对象的子类,显而易见,它是跨进程通信用的。其实它就是和系统Toast服务通信用的,应用程序需要把自己的Toast请求发送给系统Toast服务,告诉它,我需要弹一个什么样的Toast。
所以需要TN这个东东。

而TN这个东东需要初始化Handler成员变量,所以就会调用到Handler初始化这边。

4.  Handler初始化
01 public Handler(Callback callback, boolean async) {
02          
03  
04         mLooper = Looper.myLooper();
05         if (mLooper == null) {
06             throw new RuntimeException(
07                 "Can't create handler inside thread that has not called Looper.prepare()");
08         }
09          
10     }
Handler初始化的时候会拿出与当前线程相关的Looper对象,如果不存在,也就是没有调用过Looper.prepare()方法的话,就会报错。

这就是上面错误的来源啦!

三 如何解决?

知道原因后,就比较好解决了,于是就出现了下面的代码:




在分线程弹出Toast之前把Looper准备好,实验表明,这样确实可以达到效果。可以把Toast弹出来。

但是这样有个问题,就是会导致分线程永远不会退出。这个原理大家看看Looper.loop()是怎么实现的就知道了,它里面是一个死循环。

这样的话,会导致进程里面的线程越来越多,这个本人是不推荐的。

四 另外一种解决方法

除了第三种方法之外,我们还有种方法,就是把Toast丢到主线程去弹出来。

也很简单,主要初始化一个主线程的Handler,然后调用Handler.postRunnable()方法就可以了。,如下:




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值