Crach描述:
在子线程中 调用了这句:
Toast.makeText(this, "", Toast.LENGTH_LONG) .show();
然后就崩溃了:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
// at android.os.Handler.<init>(Handler.java:121)
// at android.widget.Toast$TN.<init>(Toast.java:361)
// at android.widget.Toast.<init>(Toast.java:97)
// at android.widget.Toast.makeText(Toast.java:254)
分析:跟踪源码
查看makeText方法,可以看到 new了一个Toast实例
public static Toast makeText(Context context, CharSequence text,
@Duration int duration) {
Toast result = new Toast(context);
LayoutInflater inflate = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(
com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView) v
.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
}
Toast构造函数中又实例化了一个 TN对象
public Toast(Context context) {
mContext = context;
mTN = new TN();
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}
在静态类TN中,可以读到这一句:
final Handler mHandler = new Handler();
创建了一个Handler对象。
private static class TN extends ITransientNotification.Stub {
final Runnable mShow = new Runnable() {
@Override
public void run() {
handleShow();
}
};
final Runnable mHide = new Runnable() {
@Override
public void run() {
handleHide();
// Don't do this in handleHide() because it is also invoked by handleShow()
mNextView = null;
}
};
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
final Handler mHandler = new Handler();
读 Handler构造函数:
/**
* * Default constructor associates this handler with the {@link Looper} for
* the * current thread. * * If this thread does not have a looper, this
* handler won't be able to receive messages * so an exception is thrown.
*/
public Handler() {
this(null, false);
}
继续往下读:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass
.isLocalClass())
&& (klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG,
"The following Handler class should be static or leaks might occur: "
+ klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
看到这几句应该就恍然大悟了吧。
主线程中创建handler后会默认创建一个looper对象。但是子线程不会,需要手动创建。其ThreadLoacl中没有设置过Looper,mLooper==null ,所以会抛出异常。
解决方法
方法一:增加Looper.prepare();
new Thread() {
public void run() {
Log.i("log", "run");
Looper.prepare();
Toast.makeText(ActivityTestActivity.this, "toast", 1).show();
Looper.loop();// 进入loop中的循环,查看消息队列
};
}.start();
方法二:post 给主线程去处理
// if current thread is background thread
// post show-toast-runnable to main handler
// if current thread is background thread
// post show-toast-runnable to main handler
mainHandler.post(new Runnable() {
@Override
public void run() {
if (toast == null) {
toast = Toast.makeText(context, "", Toast.LENGTH_SHORT);
}
toast.setText(msg);
toast.setDuration(Toast.LENGTH_SHORT);
toast.show();
}
});