Android進階:六、在子線程中直接使用 Toast 及其原理

最近因爲個人原因,很久沒有更文章,感謝關注的小夥伴,望諒解!

一般我們都把Toast當做一個UI控件在主線程顯示。但是有時候非想在子線程中顯示Toast,就會使用Handler切換到主線程顯示。

但是子線程中真的不能直接顯示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。

寫在最後

在最後,我整理了一份資料,如果有需要學習的同學可以聯繫我免費分享出來的,而且我們爲了感謝很多支持的學者,在騰訊課堂每晚上20點有免費的直播教學,需要的同學可以來學習學習
領取方式:交流三羣820655513


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章