通常咱們都把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