Android在子線程更新View

剛開始做Android開發時,一不小心就會在非UI線程中做更新UI的操作,從而造成崩潰,那么這是什么原因呢?那是因為ViewRootImpl在添加View的時候通過以下代碼做了線程檢測

  void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

很清楚地看到mThread如果不等于當前線程就會拋出異常,而mThread地賦值是在ViewRootImpl構造函數中完成的,所以我們就猜測如果構造ViewRootImpl的線程等于當前線程就可以在子線程中更新View了。下面我們通過一下代碼驗證。
代碼分為兩步
步驟一:在子線程中新建ViewRootImpl;
步驟二:在子線程中更新View;

 private void addViewFromThread() {
        new Thread(() -> {
            Looper.prepare();
            //獲取WindowManager實例
            //步驟一
            WindowManager wm = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);

            //設置LayoutParams屬性
            WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
            //寬高尺寸
            layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
            layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
            layoutParams.format = PixelFormat.TRANSPARENT;
            //設置背景陰暗
            layoutParams.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
            layoutParams.dimAmount = 0.6f;
            //Window類型
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
            } else {
                layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
            }
            //構造TextView
            TextView myView = new TextView(this);
            myView.setText("hello window");
            //設置背景為紅色
            myView.setBackgroundColor(Color.RED);
            FrameLayout.LayoutParams myParam = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 400);
            myParam.gravity = Gravity.CENTER;
            myView.setLayoutParams(myParam);

            //myFrameLayout 作為rootView
            FrameLayout myFrameLayout = new FrameLayout(this);
            //設置背景為綠色
            myFrameLayout.setBackgroundColor(Color.GREEN);
            myFrameLayout.addView(myView);
            //添加到window
            wm.addView(myFrameLayout, layoutParams);
            //步驟二
            Handler handler = new Handler(Looper.myLooper());
            handler.postDelayed(()->{myView.setText("子線程更新view");},5000);
            Looper.loop();
        }).start();

    }

驗證結果如下圖,結果符合預期,在子線程中實現了UI的添加和更新:


01.png

總結

1.Android更新UI操作的線程要和創建ViewRootImpl的線程一致;
2.當前線程要開啟了Loop循環,因為UI刷新依賴了Handler機制;

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容