Toast 和 Looper,一个属于 android.widget,一个属于 android.os,两个貌似联系不怎么紧密的类,却通过下面这个异常联系到了一起:
E/AndroidRuntime( 1819): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
E/AndroidRuntime( 1819): at android.os.Handler.<init>(Handler.java:121)
E/AndroidRuntime( 1819): at android.widget.Toast.<init>(Toast.java:397)
E/AndroidRuntime( 1819): at android.widget.Toast.makeText(Toast.java:230)
E/AndroidRuntime( 1819): at android.widget.Toast.makeText(Toast.java:256)
由以上的错误信息可以看出:程序要创建 handler,但是发现 Looper.prepare 还没有被调用。通过 Android SDK 中的 Reference 可以看到,Looper、Handler 的调用是非常有讲究的,如下面示例代码:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
言归正题,继续寻找 Toast、Looper 和 Handler 三者之间的联系,也许谜底就能解开了。欲揭谜底,从源码入手是一条捷径。
Toast.java 的第230行的代码是创建一个新的 Toast 实例,而实例化的过程中,就需要执行第397行,也就是声明并创建 Handler 的实例。那么来看 Handler.java 的第121行到底做了什么,如下所示:
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
到此,距离真相的解开近了一大步,既然抛出了 RuntimeException,那么 mLooper 肯定是 null,但是为什么 Looper.myLooper() 会返回 null?继续进入到 Looper.java 中寻根究底。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
以上就是 myLooper() 方法的真实面貌,通过注释可以看出问题的真正原因在于当前线程并没有绑定 Looper,返回为 null 是正确但非正常的结果。
问题的根本原因已经解开,但是另外一个疑团也就产生了:为何当前线程没有 Looper 呢?经过对代码进行 review,原因找到了:当事者在 non-UI 线程进行 Toast.makeText
分享到:
相关推荐
Toast并不是以独占方式显示的,它并不会抢夺用户的焦点,在弹出Toast的时候,依然可以对之前的界面进行操作,我们在“”一文中介绍了纯文本的Toast的使用,我们完全可以自定义Toast的显示内容和显示位置 要自定义...
Android 演示简单toast和带图片toast的实现方法,这些toast在平时的Android应用开发中使用频繁,本源码演示了两种最实用toast的用法,一种是不带图片,另一种是带图片: // 简单的toast,不带图片的实现方法: ...
toast 和notification 的学习 很适合初学android的
自定义Toast,设置Toast显示位置,自定义Toast的复杂布局 博客地址:http://blog.csdn.net/xy_nyle/article/details/20137953
android中Toast和Notification的应用.
实际上用户本意只是想关闭Notification,但是Toast的show方法中有调用INotificationManager这个类,而这个类在用户关闭消息通知权限的同时被禁用了,所以我们的吐司无法显示。 Toast.show() 效果图 自定义Toast...
vue 中简单封装类似 Toast 的消息提示插件,然后在 vue 中任意位置都可以简单使用 Toast 消息显示最前,并且默认 3 秒后自动消失 实现原理 1、vue 创建 Toast 消失提示组件的显示界面 2、js 代码动态引入 Toast 的 ...
小米mimu系统,会对toast进行拦截,在用户提交的toast 消息体拼接一个前缀,由于该种会导致插件工程出现资源错乱,获取appLabel异常,现我们通过hook 动态代理,对消息发送做一个劫持,修改消息信息,还原原来的消息
toast和context 消息栏 消息提示 通知
PhoneGap Android插件 调用toast 和 notification 里面是 java js 源码和配置文件. 使用方式和其他phonegap插件一致
全局Toast工具类,作用:连续显示toast提示时取消上一个toast 通过ToastUtil.toast(Context(), "文本")调用
Toast追踪器-Toast弹出通知来源查看
默认Toast(大家都会),自定义Toast位置,自定义Toast样式
Toast的基本使用代码,对应博客中2.1 Toast(吐司)的基本使用。
自定义时间的Toast 实现一个可以自定义时间的Toast控件MyToast。具体使用请看里面的README
用于React的TOAST UI日历这是一个包装的React组件。 :triangular_flag: 目录事件拉取请求步骤文件资料贡献执照 收集有关使用开源的统计信息TOAST UI Calendar的React Wrapper应用Google Analytics(分析)(GA)来...
Toast("toast-----2", "Arial.ttf", 0, -400, 700, 200, 48, Color.white, 1.5f); // 多个参数,可以设置大小、位置、颜色和销毁时间等等 Toast("666", "Arial.ttf", new Vector2(0, -400), new Vector2(700, 200), ...
android Toast对象的使用 自定义Toast
Android:常用Toast详解:系统自带Toast,自定义位置Toast,自定义布局Toast,静态Toast