Android 關於非主線程不能操作UI的認識,androidui
Android在應用裡顯示Dialog是個很簡單的事情,但是一直沒試過在Service裡面顯示Dialog。根據之前的經驗UI操作要在主線程,本地的服務Service是主線程裡沒錯,但是遠程service裡面顯示Dialog,聽起來是不是就應該沒有在主線程裡面了呢?
嘗試一下就知道了,寫了個AIDL的調用,client端去調用AIDL,在Service這邊就是去顯示一個Dialog。AIDL的部分就忽略了。Service這邊的代碼就和Activity上顯示dialog一樣。
AlertDialog.Builder builder = new AlertDialog.Builder(mContext); builder.setTitle("TEST"); builder.setMessage("test"); builder.show();
當然dialog要setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
結果呢,悲劇了。
Can't create handler inside thread that has not called Looper.prepare()
什麼意思呢,主觀理解,要顯示個Dialog需要有訊息迴圈的支援,給它一個訊息迴圈就好了唄。但是,問題來了。。。 這個過程發生在哪個線程上呢,後來調試發現原來每次binder調用過來都會有一個線程出現,而且還是每次都是不一樣的,估計是從一個線程池裡面拿的。
那怎麼給這個線程加上looper呢,沒辦法只能自己開一個線程了,果然在一個帶looper的線程裡去顯示dailog貌似就沒問題了。
那麼問題又來了,為什麼顯示Dialog需要looper的支援呢?
看代碼:
原來Dialog有一個
private final Handler mHandler = new Handler();
還有一個
mListenersHandler = new ListenersHandler(this);
看起來這兩個Handler都是長在當前這個線程上的,那就明白了為什麼show Dialog一定要looper了吧。
最後還有一個問題,一直說UI操作必須要在主線程,那上面說的這個情況就有點奇怪了,顯現Service是遠端,顯示dialog又是Service的一個子線程,跟主線程有半毛錢關係嗎?費解了,以我個人理解,這個非主線程不操作UI看來並不是絕對的吧。
再仔細想想,之前有看到過,其實無論是Dialog還是Acitivty本質上都是通過WindowManager往window上加了一個view(ViewRoot),所有的view不可能是只屬於一個client,各個client都在這個window上分了一杯羹,那麼,有多個線程會去更新各自的view也就不奇怪了。只是每一個View本身只能有一個線程來操作罷了。這就是我對非主線程不能操作UI的認識,不知道是不是正確。
Android的非主線線程不可以直接更改UI線程問題
線程定義在主線程中,也是兩個線程,在具體的run方法中可以用主線程內定義的變數,不過不能修改UI,要使用runonuithread方法或者用handler來做。
android非主線程更改UI異常
修改UI必須是在主線才可以
子線程可以通過handler把訊息post到主線程中