安卓在子线程中使用Toast
问题描述:
在安卓中,ui更新必须在主线执行,Toast是一种用于在屏幕上短暂显示消息的机制,它依赖主线程来进行创建和显示消息。当我们在子线程中直接调用Toast时,就会触发Can‘t create handler inside thread that has not called Looper报错。这个错误时由于Toast内部使用了Handler来处理消息队列,并在主线程中显示Toast,而在子线程中没有默认的Looper对象可供Handler使用。
当我们调用Toast的show方法时,会通过enqueueToast方法将toast插入消息队列中。
public void show() {
if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
checkState(mNextView != null || mText != null, "You must either set a text or a view");
} else {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
}
INotificationManager service = getService();
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
final int displayId = mContext.getDisplayId();
try {
if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
if (mNextView != null) {
// It's a custom toast
service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
} else {
// It's a text toast
ITransientNotificationCallback callback =
new CallbackBinder(mCallbacks, mHandler);
service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
}
} else {
service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
}
} catch (RemoteException e) {
// Empty
}
}
解决方案
新建一个Handler,用于子线程中使用Toast。
public class HandlerUtil {
private static Handler mHandler;
public static void init() {
mHandler = new Handler();
}
public static void post(MyFunction func) {
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
mHandler.post(new Runnable() {
@Override
public void run() {
func.apply();
}
});
}
}
@FunctionalInterface
public interface MyFunction {
void apply();
}
}
在application中调用初始化
HandlerUtil.init();
在子线程中实现Toast。
HandlerUtil.post(()-> ToastUtil.showToast("test", 2000));
ToastUtil类可参考:安卓Toast避免重复显示