Android:子執行緒到底能不能更新UI?
阿新 • • 發佈:2020-07-17
問題由來
我們知道,Andoird由於修改UI是執行緒不安全的,只能在主執行緒中修改。如果多個執行緒修改UI肯定會花屏,於是谷歌做了限制,只能在主執行緒中修改UI。但是有次我在子執行緒中修改了UI沒彈異常。
先來看兩段程式碼
//正常執行
btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public voidrun() { resultTv.setText("更新TextView"); } }).start(); } });
閃退,控制檯異常為:Only the original thread that created a view hierarchy can touch its views.
//彈出異常
btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {new Thread(new Runnable() { @Override public void run() { resultTv.setText("更新TextView\n");//這裡不一樣,多了個換行符 } }).start(); } });
原始碼解讀
之前的部落格有解讀ViewRootImpl是負責View的繪製,在requestLayout這個方法中會檢查是否是當前執行緒。所以只要子執行緒修改UI但不改變UI佈局時,不會彈出非主執行緒的異常。
@Overridepublic 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."); } }
那麼問題來了,假設我在onCreate的時候修改UI,layout也變了,為什麼沒報錯呢?
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); new Thread(new Runnable() { @Override public void run() { resultTv.setText("onCreate\n"); } }).start(); }
在ActivityThread中發現,ViewRootImpl是在onResume的時候被初始化的,上面那段程式碼sleep久一點等ViewRootImpl初始化完畢就會報錯
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager();//ViewRootImpl在這裡被初始化 WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); } } }
總結
- 子執行緒可以在部分情況下修改UI,如不改變佈局,在onResume之前
- 不推薦在子執行緒中修改UI