問題:“Android只能在UI線程更新UI 么?”
答:“對(duì)!......,嗯?不對(duì)?”
我腦子里的的回答是“對(duì)”,但是辯證思維又在提醒我可能有陷阱,于是我就說“大部分情況是的”。那么小部分情況呢?具體說不上來了!于是才發(fā)現(xiàn)這個(gè)問題一直被忽略了。
于是試驗(yàn)檢驗(yàn)真理,擼代碼驗(yàn)證了一遍。
new Thread(){
@Override
public void run() {
super.run();
btn_demo1.setText("Demo1--"+Thread.currentThread().getName());
}
}.start();
奔潰信息:

問題出現(xiàn)在ViewRootImpl.checkThread()的時(shí)候出錯(cuò)
查看ViewRootImpl的源碼,導(dǎo)致問題的原因:
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
mThread = Thread.currentThread();
.......
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
CalledFromWrongThreadException提示:只能在創(chuàng)建View的線程里操作view.
那么意思是我在非主線程創(chuàng)建View,就可以在非主線程操作該view了咯!
于是:
private void addWindView(){
TextView tx = new TextView(MainActivity.this);
tx.setText("今天天氣很好哦!");
tx.setTextColor(getResources().getColor(R.color.white));
tx.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
tx.setGravity(Gravity.CENTER);
WindowManager wm = MainActivity.this.getWindowManager();
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
250, 150, 200, 200, WindowManager.LayoutParams.FIRST_SUB_WINDOW,
WindowManager.LayoutParams.TYPE_TOAST, PixelFormat.OPAQUE);
wm.addView(tx, params);
}
new Thread(){
@Override
public void run() {
super.run();
addWindView();
}
}.start();
不幸的是,還是崩了。
崩潰信息:

源碼中:
void checkThread() 通過了,可是在scheduleTraversals()刷新UI的時(shí)候:
final ViewRootHandler mHandler = new ViewRootHandler();
.......
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
所以沒有Looper實(shí)例化的異常。
于是加上Looper.perpare(),和Looper.loop().
new Thread(){
@Override
public void run() {
super.run();
Looper.perpare()
addWindView();
Looper.loop()
}
}.start();
這下沒有報(bào)錯(cuò)并且成功加載顯示UI.

所以,Android中非UI主線程能不能操作UI?答案是可以的。只不過只能在創(chuàng)建View的線程里操作view.
總結(jié):
1.由于Android是通過Handler消息機(jī)制的方式刷新UI的。所以Android 的UI控件是線程安全的,不會(huì)導(dǎo)致多線程訪問使得UI處于不可預(yù)期的狀態(tài)。
2.Android每次刷新UI的時(shí)候,最終根布局ViewRootImpl.checkThread()來檢驗(yàn)線程是否是View的創(chuàng)建線程。