Android在子线程更新View

刚开始做Android开发时,一不小心就会在非UI线程中做更新UI的操作,从而造成崩溃,那么这是什么原因呢?那是因为ViewRootImpl在添加View的时候通过以下代码做了线程检测

csharp 复制代码
  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;

ini 复制代码
 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机制;

相关推荐
weixin_440784113 小时前
【HandlerThread实现原理】
android·java·开发语言
唐诺5 小时前
Android USB ADB 调试方案完全解析
android·adb·usb
寰宇星城9 小时前
AppActuator - 让 AI Agent 直接理解并操作运行中的 Android App
android
波罗丁牌10 小时前
uni-app 实现视频聊天、屏幕分享,支持Android、HarmonyOS、iOS
android·uni-app·音视频
峥嵘life10 小时前
Android WiFi 热点 Channel 信道 和 Frequency 频率 转换总结
android·开发语言
jieyucx10 小时前
【高级利用】条件竞争与逻辑漏洞:与服务器赛跑的艺术
android·运维·服务器·web安全·文件上传
凡泰AI12 小时前
APP同时覆盖了iOS、安卓和鸿蒙,如何选择混合开发架构才能减少重复建设,提高功能上线效率~
android·ios·harmonyos·mpaas·uni·小程序容器
叶辞树12 小时前
死锁问题分析
android
TimeFine13 小时前
让 Codex “用上”Android Studio:JetBrains MCP Server 接入与实践
android
__Witheart__14 小时前
3568 Android 设置首次烧录后 usb otg 口模式
android·adb·rockchip