美文网首页Android大前端开发
Android在子线程更新View

Android在子线程更新View

作者: 大聪明的博客 | 来源:发表于2022-07-26 09:34 被阅读0次

    刚开始做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机制;

    相关文章

      网友评论

        本文标题:Android在子线程更新View

        本文链接:https://www.haomeiwen.com/subject/qsfoirtx.html