今天用子線程調Toast報了一個Can't create handler inside thread that has not calledLooper.prepare()錯誤。在網上找了找答案,發現兩篇文章:html
【1】http://czhjchina.blog.163.com/blog/static/20027904720123163149186/java
由於toast的現實須要在activity的主線程才能正常工做,因此傳統的線程不能使toast顯示在actvity上,經過Handler可使自定義線程運行於Ui主線程
前幾回碰到這個問題,確實鬱悶了好久... log -- java.lang.RuntimeException: Can't create handler inside thread that has not calledLooper.prepare()
解決辦法很簡單:
Looper.prepare();
Toast.makeText(getApplicationContext(), "test", Toast.LENGTH_LONG).show();
Looper.loop();
爲何要加這兩句,看了源碼就瞭解了
Toast
public void show() {
...
service.enqueueToast(pkg, tn, mDuration); //把這個toast插入到一個隊列裏面
...
}
Looper
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper()); //在當前線程中建立一個Looper
}
private Looper() {
mQueue = new MessageQueue(); //關鍵在這,建立Looper都幹了什麼。 實際上是建立了消息隊列
mRun = true;
mThread = Thread.currentThread();
}
通常若是不是在主線程中又開啓了新線程的話,通常都會碰到這個問題。
緣由是在建立新線程的時候默認狀況下不會去建立新的MessageQueue。
總結下:Toast 顯示的必要條件:
1:Toast 顯示須要出如今一個線程的消息隊列中.... 很隱蔽android
【2】http://tech.cncms.com/shouji/android/96016.html多線程
Android中HandlerThread類的解釋ide
Android應用中的消息循環由Looper和Handler配合完成,Looper類用於封裝消息循環,類中有個MessageQueue消息隊列;Handler類封裝了消息投遞和消息處理等功能。函數
系統默認狀況下只有主線程(即UI線程)綁定Looper對象,所以在主線程中能夠直接建立Handler的實例,可是在子線程中就不能直接new出Handler的實例了,由於子線程默認並無Looper對象,此時會拋出RuntimeException異常:oop
瀏覽下Handler的默認構造函數就一目瞭然了:this
若是須要在子線程中使用Handler類,首先須要建立Looper類實例,這時能夠經過Looper.prepare()和Looper.loop()函數來實現的。閱讀Framework層源碼發現,Android爲咱們提供了一個HandlerThread類,該類繼承Thread類,並使用上面兩個函數建立Looper對象,並且使用wait/notifyAll解決了多線程中子線程1獲取子線程2的Looper對象爲空的問題。spa
第一篇文章的處理方法是對的,可是解釋應該是第二篇文章所說的,Toast建立時須要建立一個Handler,可是這個Handler須要得到Looper的實例,而在子線程中是沒有這個實例的,須要手動建立。線程
附Toast部分源碼:
public Toast(Context context) {
mContext = context;
mTN = new TN();
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
}
private static class TN extends ITransientNotification.Stub {
……
final Handler mHandler = new Handler();
……
}
Handler源碼:
/** * Default constructor associates this handler with the queue for the * current thread. * * If there isn't one, this handler won't be able to receive messages. */ public Handler() { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null; }