Android進階:六、在子線程中直接使用 Toast 及其原理
但是子線程中真的不能直接顯示Toast嗎?
答案是:當然可以。
那應該怎麽操作呢?在當前線程中先初始化一個Looper即可!
Looper.prepare();
Toast.makeText(getBaseContext(), "text", Toast.LENGTH_LONG).show();
Looper.loop();
為什麽在子線程中使用Toast需要初始一個Looper呢? 我們看看源代碼:
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在用,我們看看它的構造函數:
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。
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。
寫在最後
如果喜歡我的文章,想與一群資深開發者一起交流學習的話,歡迎加入我的合作群Android Senior Engineer技術交流群。有flutter—性能優化—移動架構—資深UI工程師 —NDK相關專業人員和視頻教學資料
群號:925019412
Android進階:六、在子線程中直接使用 Toast 及其原理