Toast不能直接在子線(xiàn)程中使用

new Thread(){
    public void run(){
        Toast.makeText(public_log.this,"圖片不存在",Toast.LENGTH_SHORT).show();
    }
}

如果像這樣直接在子線(xiàn)程中彈出Toast,程序會(huì)報(bào)錯(cuò)。

深入源碼了解一下原因:

  public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
    Toast result = new Toast(context);

    LayoutInflater inflate = (LayoutInflater)
            context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
    TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
    tv.setText(text);

    result.mNextView = v;
    result.mDuration = duration;

    return result;
}

makeText方法好像沒(méi)有什么不對(duì),那么繼續(xù)向下看show()方法

   public void show() {
    if (mNextView == null) {
        throw new RuntimeException("setView must have been called");
    }

    INotificationManager service = getService();
    String pkg = mContext.getOpPackageName();
    TN tn = mTN;
    tn.mNextView = mNextView;

    try {
        service.enqueueToast(pkg, tn, mDuration);
    } catch (RemoteException e) {
        // Empty
    }
}

好像沒(méi)什么不對(duì) 但是看getService()不對(duì)勁就點(diǎn)擊進(jìn)去看一下

  static private INotificationManager getService() {
    if (sService != null) {
        return sService;
    }
    sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
    return sService;
}

這里還是看不出有什么問(wèn)題 然而show()里面這個(gè)又一次引起了我的注意

TN tn = mTN;

我點(diǎn)擊進(jìn)去查看源碼,好家伙終于發(fā)現(xiàn)問(wèn)題所在了

 private static class TN extends ITransientNotification.Stub {
    final Runnable mShow = new Runnable() {
        @Override
        public void run() {
            handleShow();
        }
    };

    final Runnable mHide = new Runnable() {
        @Override
        public void run() {
            handleHide();
            // Don't do this in handleHide() because it is also invoked by handleShow()
            mNextView = null;
        }
    };

    private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
    final Handler mHandler = new Handler();

    。。。。。代碼省略。。。。。。


    /**
     * schedule handleShow into the right thread
     */
    @Override
    public void show() {
        if (localLOGV) Log.v(TAG, "SHOW: " + this);
        mHandler.post(mShow);
    }

    /**
     * schedule handleHide into the right thread
     */
    @Override
    public void hide() {
        if (localLOGV) Log.v(TAG, "HIDE: " + this);
        mHandler.post(mHide);
    }

    。。。。。代碼省略。。。。。。
}

我相信聰明的你們應(yīng)該看到了這里為什么錯(cuò)了,對(duì),就是Handler不能再子線(xiàn)程里運(yùn)行的 因?yàn)樽泳€(xiàn)程沒(méi)有創(chuàng)建Looper.prepare(); 所以就報(bào)錯(cuò)了。主線(xiàn)程不需要調(diào)用,是因?yàn)橹骶€(xiàn)程已經(jīng)默認(rèn)幫你調(diào)用了。

可以看到一個(gè)Toast的創(chuàng)建需要依賴(lài)Handler。那么 我不要 我不要 我一定要在子線(xiàn)程使用Toast那怎么辦。

其實(shí)很簡(jiǎn)單,它卻什么就給它什么。
第一種方法

  new Thread(){
        @Override
        public void run() {
            super.run();
            Looper.prepare();
            try {
                Toast.makeText(MainActivity.this,"ceshi",Toast.LENGTH_SHORT).show();
            }catch (Exception e) {
                Logger.e("error",e.toString());
            }
            Looper.loop();
        }
    }.start();

因?yàn)槌薃ctivity ui線(xiàn)程默認(rèn)創(chuàng)建之外,其他線(xiàn)程不會(huì)自動(dòng)創(chuàng)建調(diào)用 Looper.prepare()來(lái)給線(xiàn)程創(chuàng)建消息循環(huán),然后再通過(guò),Looper.loop()來(lái)使消息循環(huán)起作用。

第二種方法就是

  runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(MainActivity.this,"ceshi23333",Toast.LENGTH_SHORT).show();
        }
    });
    new Thread(){

    }.start();

Toast的代碼創(chuàng)建在Runnable中,然后在需要Toast時(shí),把這個(gè)Runnable對(duì)象傳給runOnUiThread(Runnable)。 這樣Runnable對(duì)像就能在ui程序中被調(diào)用。如果當(dāng)前線(xiàn)程是UI線(xiàn)程,那么行動(dòng)是立即執(zhí)行

第三種方法和第一張差不多

  Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        //這里寫(xiě)你的Toast代碼
        }
    };

    new Thread(){
        @Override
        public void run() {
            super.run();
            mHandler.sendEmptyMessage(0);
        }
    }.start();

另:在非主線(xiàn)程中直接new Handler() 會(huì)報(bào)如下的錯(cuò)誤: E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 原因是非主線(xiàn)程中默認(rèn)沒(méi)有創(chuàng)建Looper對(duì)象,需要先調(diào)用Looper.prepare()啟用Looper。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容