这个问题先前没怎么注意,只是知道Toast会有类似于一个队列的东西,你每调用一次都会加入队列,
先进先出。今天正好趁解决这个问题来详细研究一下Toast的实现原理。
这个问题一般出现在子线程没有初始化Looper时调用Toast时出现,既然没有初始化Looper那就给
他初始化或者让他在UI线程中弹吐司就可以了,话不多说直接给出解决方案:
方案一:在子线程中初始化Looper
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
Looper.prepare();
Toast.makeText(MainActivity.this,"hello world!",Toast.LENGTH_SHORT).show();
Looper.loop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
方案二:运行在主线程中
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"hello world!",Toast.LENGTH_SHORT).show();
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
问题解决了,现在可以看一下为什么会出现这种问题了,弹吐司的标准写法是 Toast.makeText(context,"hello world!",Toast.LENGTH_SHORT).show();
我们先看一下makeText这个方法的实现:
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;
}
这个方法比较明了就是进行布局的加载,这个布局里面也很简单,LinearLayout包裹着一个TextView,
看到这里并没有看到任何与问题相关的东西,接着看。
控制Toast显示和隐藏的方法是.show()/hide();
看看show()方法
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
//JNI调用
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
//着重看这个
TN tn = mTN;
tn.mNextView = mNextView;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}
TN这个静态内部类代码较多,直接看重点就好了
private static class TN extends ITransientNotification.Stub {
final Runnable mHide = new Runnable() {
@Override
public void run() {
handleHide();
mNextView = null;
}
};
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
//使用handler进行显示隐藏处理
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
IBinder token = (IBinder) msg.obj;
handleShow(token);
}
};
@Override
public void show(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.obtainMessage(0, windowToken).sendToTarget();
}
@Override
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
}
可以看出来Toast的show和hide是通过Handler进行处理的,而handler是需要MessageQueue来
管理handler消息,如果没有的话就会报错了,主线程中可以使用的原因是ActivityThread的main()
方法中对Looper进行了初始化,Looper.prepareMainLooper();这里也是程序的入口。
到这里就分析完了,下一篇文章看看能不能对深入理解一下Toast的实现吧。
Toast使用报错java.lang.RuntimeException: Can't create handler inside thread 原因及解决方案(一)
最新推荐文章于 2024-08-05 20:54:15 发布
本文解析了在子线程未初始化Looper时调用Toast导致的问题,并提供了两种解决方案:在子线程中初始化Looper或确保操作运行在主线程中。深入探讨了Toast的实现机制,包括其依赖的Handler与MessageQueue。
121

被折叠的 条评论
为什么被折叠?



