安卓开发都知道 Toast不能在子线程使用
Toast的使用方式:Toast.makeText(context, msg, Toast.LENGTH_LONG).show()。
当在非UI线程使用时会抛出:Can't create handler inside thread that has not called Looper.prepare()
从异常可以看出直接原因是没有调用Looper.prepare(),所以可以这样写
Looper.prepare();
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
Looper.loop();
那么为什么不能在非UI线程使用呢?查看源码
/**
* 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
}
}
Tn对象的show:
/**
* schedule handleShow into the right thread
*/
@Override
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.post(mShow);
}
可以看到调用了Handler的post方法,然而:
final Handler mHandler = new Handler();
handler使用了当前线程的looper,因为工作线程若没有初始化其Looper,也就没有MessageQueue来管理Handler消息,Handler的post()方法将会返回失败(false)。
创建通用Toast
我们可以根据源码来设计一个非UI线程的Toast,方法是使用UI线程的looper,
创建handler:
public static Handler handler=new Handler(Looper.getMainLooper());
此时的show是这样的
public static void showToast(final Context context,
final String content) {
if (Looper.myLooper()==Looper.getMainLooper())
{//主线程
show(context,content);
}else
{//子线程
handler.post(new Runnable()
{
@Override
public void run()
{
show(context,content);
}
});
}
}
根据当前的lopper是否为UI的选择弹出方式是直接显示还是高仿源码调用主线程的handler.post()方法显示。
public static void show(Context context,String content){
if (TextUtils.isEmpty(content)) {
return;
}
//toast 为全局的 private static Toast toast;
if (toast == null) {
toast = Toast.makeText(context,
content,
Toast.LENGTH_SHORT);
} else {
toast.setText(content);
}
toast.show();
}
此处,判断Toast是否为null,避免了多次调用反复弹出,根据个人需要修改即可。