因為Toast在service和activity中都可以執行。所以開始就認為和ui線程沒有有太大的關系,而現在子線程Toast竟然報錯!無奈之下,花了半天的時間看了一下Handler,Looper,Toast的源碼,終於搞定了。(這個效率..本人愚鈍啊)----->的確和UI線程沒有關系
記錄下來,希望對遇上同樣問題的同學有所幫助。下面正題
1、錯誤的關鍵位置在於Toast初始化的時候,這句
public class Toast {final Handler mHandler = new Handler();….}
2、其實在別的地方也看到過,普通線程不能直接new一個Handler
原因:
public Handler(){
…..
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
…..
}
3、而Looper中
public static final Looper myLooper() {
//這個方法是從當前線程的ThreadLocal中拿出設置的looper
return (Looper)sThreadLocal.get();
}
而事實上子線程只是一個普通的線程,其ThreadLoacl中沒有設置過Looper,所以會拋出異常
4、解決方法
public void onClick(View v) {
new Thread(){
public void run() {
Log.i("log", "run");
Looper.prepare();
Toast.makeText(ActivityTestActivity.this, "toast", 1).show();
Looper.loop();// 進入loop中的循環,查看消息隊列
};
}.start();
}
Looper.prepare()方法參考
//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());
}
下邊的可以忽略了
5、至於內部的通訊機制就不知道了
只知道show()方法裡邊調用了InotificationManager. enqueueToast(pkgName, tn, mDuration)
其中tn是繼承了ItransientNotification.Stub的遠程通信接口,而handler也是在這個TN類中調用!猜想內部機制也是NotificationService的進程間通信機制!
下邊代碼,算是管中窺豹吧
-----源碼不是這個樣子的,被我概括
ItransientNotification中有個show方法
public void show() {
if (localLOGV) Log.v(TAG, "SHOW: " +this);
Handler.post(mShow);
}
其中mshow是
final Runnable mShow = new Runnable() {
public void run() {
…..
WindowManagerImpl mWM = WindowManagerImpl.getDefault();
mWM.addView(mView, mParams);
…..
}
};