通常咱们都把Toast当作一个UI控件在主线程显示。可是有时候非想在子线程中显示Toast,就会使用Handler切换到主线程显示。bash
可是子线程中真的不能直接显示Toast吗?函数
答案是:固然能够。oop
那应该怎么操做呢?在当前线程中先初始化一个Looper便可!ui
Looper.prepare();
Toast.makeText(getBaseContext(), "text", Toast.LENGTH_LONG).show();
Looper.loop();
复制代码
为何在子线程中使用Toast须要初始一个Looper呢? 咱们看看源代码:spa
public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
return makeText(context, null, text, duration);
}
public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
@NonNull CharSequence text, @Duration int duration) {
Toast result = new Toast(context, looper);
...
return result;
}
复制代码
以上是咱们使用Toast时调用的静态方法,能够看到第二个方法有个参数Looper,虽然咱们平时用的时候都传入的是null,那这个Looper究竟有什么用呢?咱们看看Toast的构造函数:线程
public Toast(@NonNull Context context, @Nullable Looper looper) {
mContext = context;
mTN = new TN(context.getPackageName(), looper);
}
复制代码
能够看出这个Looper实际上是TN在用,咱们看看它的构造函数:code
TN(String packageName, @Nullable Looper looper) {
if (looper == null) {
// Use Looper.myLooper() if looper is not specified.
looper = Looper.myLooper();
if (looper == null) {
throw new RuntimeException(
"Can't toast on a thread that has not called Looper.prepare()");
}
}
}
复制代码
以上代码有简化。能够看出当Looper为null的时候,会经过Looper.myLooper获取一个当前的Looper。咱们知道在主线程中系统已经为咱们初始化了一个mainLooper,因此咱们通常不用管。可是当咱们子线程中若是没有初始化Looper,这里调用Looper.myLooper就获取不到一个Looper,则会抛出异常。因此当咱们在子线程中使用Toast,使用Looper.prepare()方法初始化一个Looper并用Looper.loop()让它启动起来便可。内存
因此咱们能够封装一个能够在任何线程使用的Toast。ci
private static Toast toast = null;
public static void showToast(Context context, String text) {
Looper myLooper = Looper.myLooper();
if (myLooper == null) {
Looper.prepare();
myLooper = Looper.myLooper();
}
if (toast == null) {
toast = Toast.makeText(context, text, Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0, 0);
}
toast.show();
if ( myLooper != null) {
Looper.loop();
myLooper.quit();
}
}
复制代码
咱们初始化Toast以前先判断当前线程的looper是否为空,为空则初始化一个新的myLooper,而后在调用Toast的show方法以后让looper启动起来便可。由于Looper的loop()方法是无限循环的,为了防止Looper阻塞线程,致使内存泄漏应该及时退出Looper。get