Toast show的时候崩溃的问题
2016-10-21 17:58
267 查看
今天同事提交了一份代码,里面有一个简单的Toast.makeText(`````).show(),没想到真正运行的时候跑到这里挂掉了:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
从崩溃上看这是looper没有启动导致的,但是为什么? 查询源码后找到了原因(以6.0源码为例):
先看Toast的show方法:
/**
* 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
}
}
这里直接交给了INotificationManager来进行显示的操作,按照android的命名规则,这个AIDL的实现位于NotificationManagerService中:
<span style="white-space:pre"> </span>@Override
public void enqueueToast(String pkg, ITransientNotification callback, int duration)
{
......
synchronized (mToastQueue) {
int callingPid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
try {
ToastRecord record;
int index = indexOfToastLocked(pkg, callback);
// If it's already in the queue, we update it in place, we don't
// move it to the end of the queue.
if (index >= 0) {
record = mToastQueue.get(index);
record.update(duration);
} else {
// Limit the number of toasts that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
一系列的判断后,交给了showNextToastLocked方法:
void showNextToastLocked() {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try {
record.callback.show();
scheduleTimeoutLocked(record);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Object died trying to show notification " + record.callback
+ " in package " + record.pkg);
// remove it from the list and let the process die
int index = mToastQueue.indexOf(record);
if (index >= 0) {
mToastQueue.remove(index);
}
keepProcessAliveLocked(record.pid);
if (mToastQueue.size() > 0) {
record = mToastQueue.get(0);
} else {
record = null;
}
}
}
}
最终的展示是在这句:record.callback.show(),这个record.callback其实是由Toast通过AIDL传递过来的,回头再看Toast的代码,其实是调用了一个内部类TN.show():
/**
* schedule handleShow into the right thread
*/
@Override
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
}
而这个mHandler也就是我们报错时所提示的looper所在,它的创建就是一句简单的new而已:
private static class TN extends ITransientNotification.Stub {
至此也就搞清楚了这个崩溃的根本原因了,运行Toast.show()的线程,自身的looper并没有开始工作,所以导致了这个崩溃。
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
从崩溃上看这是looper没有启动导致的,但是为什么? 查询源码后找到了原因(以6.0源码为例):
先看Toast的show方法:
/**
* 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
}
}
这里直接交给了INotificationManager来进行显示的操作,按照android的命名规则,这个AIDL的实现位于NotificationManagerService中:
<span style="white-space:pre"> </span>@Override
public void enqueueToast(String pkg, ITransientNotification callback, int duration)
{
......
synchronized (mToastQueue) {
int callingPid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
try {
ToastRecord record;
int index = indexOfToastLocked(pkg, callback);
// If it's already in the queue, we update it in place, we don't
// move it to the end of the queue.
if (index >= 0) {
record = mToastQueue.get(index);
record.update(duration);
} else {
// Limit the number of toasts that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
<span style="white-space:pre"> </span>...... record = new ToastRecord(callingPid, pkg, callback, duration); mToastQueue.add(record); index = mToastQueue.size() - 1; keepProcessAliveLocked(callingPid); } // If it's at index 0, it's the current toast. It doesn't matter if it's // new or just been updated. Call back and tell it to show itself. // If the callback fails, this will remove it from the list, so don't // assume that it's valid after this. if (index == 0) { showNextToastLocked(); } } finally { Binder.restoreCallingIdentity(callingId); } } }
一系列的判断后,交给了showNextToastLocked方法:
void showNextToastLocked() {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try {
record.callback.show();
scheduleTimeoutLocked(record);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Object died trying to show notification " + record.callback
+ " in package " + record.pkg);
// remove it from the list and let the process die
int index = mToastQueue.indexOf(record);
if (index >= 0) {
mToastQueue.remove(index);
}
keepProcessAliveLocked(record.pid);
if (mToastQueue.size() > 0) {
record = mToastQueue.get(0);
} else {
record = null;
}
}
}
}
最终的展示是在这句:record.callback.show(),这个record.callback其实是由Toast通过AIDL传递过来的,回头再看Toast的代码,其实是调用了一个内部类TN.show():
/**
* schedule handleShow into the right thread
*/
@Override
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
}
而这个mHandler也就是我们报错时所提示的looper所在,它的创建就是一句简单的new而已:
private static class TN extends ITransientNotification.Stub {
<span style="white-space:pre"> </span>......
<span style="white-space:pre"> </span>private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); final Handler mHandler = new Handler();
<span style="white-space:pre"> </span>
}
至此也就搞清楚了这个崩溃的根本原因了,运行Toast.show()的线程,自身的looper并没有开始工作,所以导致了这个崩溃。
相关文章推荐
- 调用[super setSelectedIndex:nIndex];时候崩溃问题?
- 手机拍照,在回调缩放的时候程序崩溃问题
- 最近一个崩溃的问题——针对数据量很大,和并发访问量高的时候
- 如何解决iOS中更改系统定位权限时候系统设置崩溃的问题
- iOS项目开发中,对后台返回数据为<null>时候的处理,避免取值产生程序崩溃问题
- Android 当使用相机并截取照片的时候程序崩溃的问题
- actionBar.show()崩溃问题
- iOS项目开发中,对后台返回数据为<null>时候的处理,避免取值产生程序崩溃问题
- 趣拍SDK的使用其他颜色渲染,和第二次录制的时候会崩溃问题
- FTPClient.cs (采用定时器上传ftp服务器文件的时候出现问题,程序间断性崩溃,求帮助!!)
- 在vs中用tesseract进行ocr的时候运行到init时崩溃问题
- 使用Url Rewrite 如果原始Url就带QueryString时候的一个问题
- 退出VP场景时,程序崩溃的问题
- windows media services的配置过程中权限问题[点播的时候总要密码]
- 关于您提到使用WebServices时候遇到“基础连接以关闭”的问题
- 无聊的时候,写个sql,琢磨着解决自己工作中遇到的费时的问题.
- 关于浮点数的问题,我再做一个字体设置的时候总有问题,现在看了这个文章,受到了启发
- 使用WebServices时候遇到“基础连接以关闭”的问题
- StreamBox Ripper 将rm转mp3时候出现g2支持的问题
- 在使用SPS操作文件的时候碰到这样的问题