Android基础 -- 子线程可以修改UI吗?

2023-05-16

子线程可以修改UI吗?为什么会产生这样的问题,可能是因为在开发过程中遇到了

"Only the original thread that created a view hierarchy can touch its views."

这个异常信息,又或者是常用Handler将子线程中的数据更新到UI上,又或者是其他的一些原因,如果你思考到了为什么要在主线程中更新UI或者子线程中可以更新UI吗这样的问题,说明你有一颗探寻问题本质的心,对于技术从业者来讲,是极为有益的,知其然更要知其所以然,接下来,就一起看看其中的逻辑。

先说结论,子线程是可以修改的UI的,例如下面这行代码

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.yourlayoutid);
        new Thread(new Runnable() {
            @Override
            public void run() {
                mContentTv.setText("我是来自子线程的更新信息");
            }
        }).start();
    }

但是如果给这个线程做个延迟,或者使用按钮点击来执行,就会看到上面熟悉的异常信息了,那么我们从这个异常信息入手,这个异常信息是在ViewRootImpl中抛出的

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

如果你不熟悉ViewRootImpl,先不要跳出去看ViewRootImpl,对于刚开始看源码的同学而言,切记不要一看到陌生的类,就跳出去看,在跳出去看的过程中,可能又会遇到不熟悉的类,所以,相关的类的知识点,只需要知道它的大致功能就好,等理解了当前的知识点后,在去看这些陌生的类的细节,刚开始可能会比较痛苦,但是一段时间之后,看源码的效率会大大提高。

ViewRootImpl可以先暂时理解为一个View的管理类,它在执行一些View相关操作的时候,会去检查线程是否为主线程,如果不是,就抛出这个异常。

那么接下来的关注点就是ViewRootImpl何时被创建?

既然是创建,我们就从构造方法入手

public ViewRootImpl(Context context, Display display) {
        ...        
}

可以看到在WindowManagerGlobal的addView方法里,有着ViewRootImpl的初始化操作

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        
        ViewRootImpl root;
        
        synchronized (mLock) {

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

        }
    }

而WindowManagerGlobal的addView方法会在WindowMangerImpl被调用

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

WindowManagerImpl是WindowManager的实现类,而WindowsManager继承自ViewManager,在Activity里的makeVisible方法里有着WindowMangerImpl的addView方法的调用

void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }

而makeVisible方法在ActivityThread的handleResumeActivity里进行了调用

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
       
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }

    }

在handleResumeActivity里,调用了perfromResumeActivity方法

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
      
        }

在perfromResumeActivity方法里,调用了activity的performResume方法

    @VisibleForTesting
    public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
            r.activity.performResume(r.startsNotResumed, reason);
        } 

在performResume方法里,调用了Instrument的callActivityOnResume方法

final void performResume(boolean followedByPause, String reason) {
        mInstrumentation.callActivityOnResume(this);
    }

callActivityOnResume方法里,调用了onResume

public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
        activity.onResume();
    }

至此,我们就可以明白,ViewRootImpl是在onResume的时候创建的,而上面的示例代码,是在onCreate里修改UI的,这个时候还没有ViewRootImpl,所以不会检查线程。

上面的源码分析是从入口一步一步找上来了,现在顺着逻辑在来做一个总结。

在ActivityThread里的handleResumeActivity方法里,调用了onResume,并且创建了ViewRootImpl,有了ViewRootImpl后,就无法在子线程中直接修改UI了。

这两条线,小伙伴可以自己顺着源码找一找具体的调用关系,调用到的函数在文章里都提到了,希望会对小伙伴有帮助~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android基础 -- 子线程可以修改UI吗? 的相关文章

  • 游标的概念和作用

    游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制 游标充当指针的作用 尽管游标能遍历结果中的所有行 xff0c 但他一次只指向一行 概括来讲 xff0c SQL的游标是一种临时的数据库对象 xff0c 即可以用来存放在数
  • 【STM32H750】从零编写MDK的FLM烧录算法

    文章目录 前言一 将代码中的图片资源下载到外部flash1 修改分散加载文件 二 MDK下载算法原理1 程序能够通过下载算法下载到芯片的原理2 算法程序中擦除操作执行流程3 制作FLM文件步骤 三 使用STM32CubeMX新建工程1 新建
  • 使用MDK开发树莓派pico RP2040之外部 flash下载算法

    文章目录 前言一 MDK下载算法原理1 程序能够通过下载算法下载到芯片的原理2 算法程序中操作执行流程3 创建MDK下载算法通用流程 二 树莓派pico下载算法制作步骤1 下载树莓派的MDK的程序模板2 修改输出算法文件的名字3 修改编程算
  • 单片机堆栈分配

    Code xff1a 表示程序所占用 FLASH 的大小 xff08 FLASH xff09 RO data xff1a 即 Read Only data xff0c 表示程序定义的常量 xff0c 如 const 类型 xff08 FLA
  • 【STM32F429】通过STM32CubeMX移植TouchGFX

    目录 STM32F429 移植TouchGFX到RT Thread系统 xff08 1 xff09 STM32F429 使用TouchGFX的MVP架构来实现GUI和硬件的双向交互 xff08 2 xff09 STM32F429 RT Th
  • TouchGFX使用MVP架构来实现GUI和硬件的双向交互

    目录 xff1a 实战 xff1a 1 STM32F767移植touchGFX 使用RT Thread系统实现DIY数字仪表 xff08 完成 xff09 2STM32F429移植touchGFX 使用RT Thread系统实现DIY数字仪
  • python 微信自动回复机器人

    python 微信自动回复机器人 导入wxauto https github com cluic wxauto span class token comment python3 span span class token comment c
  • 《python+opencv实践》一、基于颜色的物体追踪(上)

    点击打开链接 本文主要参考国外一大牛博客 xff0c 然后自己修改得来 相关知识点在这里 实现功能 xff1a 追踪红颜色瓶盖 xff0c 并画出瓶盖轮廓和运动轨迹 from collections import deque import
  • 《python+opencv实践》一、基于颜色的物体追踪(下)

    本文对 python 43 opencv实践 一 基于颜色的物体追踪 xff08 上 xff09 做了功能上的强化 xff0c 强化如下 xff1a xff08 1 xff09 加了pts清空 xff0c 即当没有检测到目标时 xff0c
  • Ubuntu16.04 编译出错c++: internal compiler error: Killed (program cc1plus)

    最近在使用github上的一个模拟器 xff0c 需要自己对其中文件进行make编译 但是中间遇到了不知道多少个错误 xff0c 吐血 想了想还是记录一下 xff0c 错误 compiling moc moc qwt plot panner
  • C++创建信号量 CreateSemaphore

    一 定义 Semaphore也是一个线程同步的辅助类 xff0c 可以维护当前访问自身的线程个数 xff0c 并提供了同步机制 使用Semaphore可以控制同时访问资源的线程个数 xff0c 例如 xff0c 实现一个文件允许的并发访问数
  • OpenGL ES之GLSurfaceView学习一:介绍

    原文地址 http 120 132 134 205 cmdn supesite uid 5358 action viewspace itemid 6527 GLSurfaceView是一个视图 xff0c 继承至SurfaceView xf
  • C++之STL迭代器

    一 背景 迭代器 iterator 是一种抽象的设计理念 xff0c 即迭代器模式 xff0c 通过迭代器可以在不了解容器内部原理的情况下遍历容器 除此之外 xff0c STL中迭代器一个最重要的作用就是作为容器 vector queue
  • C++make_shared的使用

    一 使用 make shared是标准库函数 xff0c 此函数在动态内存中分配一个对象并初始化它 xff0c 返回指向此对象的shared ptr 由于是通过shared ptr管理内存 xff0c 因此这是一种安全分配和使用动态内存的方
  • cmkae命令set_target_properties

    一 介绍 命令的格式如下 set target properties target1 target2 PROPERTIES prop1 value1 prop2 value2 Sets properties on targets The s
  • cmake的install

    一 介绍 一般使用cmake xff0c 常用命令就是 mkdir build amp amp cd build cmake make make install install命令为项目生成一系列的安装规则 在执行make install时
  • cmake命令之list

    一 介绍 cmake的list命令即对列表的一系列操作 xff0c cmake中的列表变量是用分号 分隔的一组字符串 xff0c 创建列表可以使用set命令 xff08 参考set命令 xff09 xff0c 例如 xff1a set va
  • Cmake之ExternalProject_Add

    一 介绍 ExternalProject命令可以很好的解决项目中使用第三方库 提高项目的可用性 ExternalProject Add 函数创建一个外部工程可以驱动下载 更新 补丁 配置 构建 安装和测试流程的自定义目标 语法 xff1a
  • POI导入Excel,获取公式的值

    直接POI导入Excel中的数据的时候 xff0c 直接获取表中的值 xff0c 如果表中单元格的值时由公式计算得出的话 xff0c 获取到的会是公式 所以需要对获取的单元格的值进行处理 xff1a 导入数字时 导入公式的计算结果而非公式
  • navicate连接远程数据库

    远程主机的3306端口一般是不允许外网直接访问的 xff0c 但是开发过程中 xff0c 使用navicate工具进行数据库操作会方便超级多 xff0c 那么要怎么配置navicate连接远程数据库呢 超简单两步走 xff1a 1 使用se

随机推荐

  • idea中Gradle项目控制台中文乱码

    我使用的是IEDA2021 xff0c 之前跑maven项目一切正常 今天导入了一个Gradle项目 xff0c debug的时候控制台中文乱码了 之前直接用idea控制台中文乱码做关键词搜索 xff0c 改了file settings e
  • @RequestMapping value值置为““

    我们通常用 64 RequestMapping来映射请求 xff0c 比如 xff0c 写一个方法 xff1a span class token annotation punctuation 64 RequestMapping span s
  • Android多媒体学习十:利用AudioRecord类实现自己的音频录制程序

    AudioRecord类相对于MediaRecorder来说 xff0c 更加接近底层 xff0c 为我们封装的方法也更少 然而实现一个AudioRecord的音频录制程序也很 简单 本实例代码如下 xff1a 可惜 xff0c 本实例测试
  • ROS系统SLAM基础学习:运行gazebo仿真建立保存地图

    ROS系统SLAM基础学习 xff1a gazebo仿真建立保存地图 使用gmapping建立并保存地图使用hector slam建立并保存地图遇到的问题解决以及总结 软件版本Ubuntu16 04LTSROSkineticgazebo7
  • 软件安装时窗口出现在屏幕左上角而且拖不出来

    今天在安装MYSQL是出现如下问题 xff1a 安装助手出现在屏幕左上角而且拖不出来 xff0c 导致安装没办法完成 用一个很简单的方法解决了问题 xff1a 桌面空白处右键 xff0c 点屏幕分辨率 把方向改成纵向 xff0c 左上角的窗
  • DELL笔记本插入耳机没反应

    新入的戴尔燃7000 xff0c 上午因为CPU占用飙升 xff0c 关掉了笔记本上的几个自启动项 xff0c 下午插入耳机就无响应了 xff0c 耳机插进去 xff0c 还是外放 百度原因 xff0c 很多都提及了Realtek这一声卡驱
  • the server responded with a status of 404 (Not Found)

    使用ajax跳转方法时 xff0c 页面ctrl 43 shift 43 i调试报告了一个404错误 xff0c 说找不到方法 页面地址栏直接指向方法的地址跳转也是404 目标方法是新增的 xff0c 于是使用复制黏贴 xff0c 确定各处
  • select设置只读

    根据需求 xff0c 需要根据后台传来的参数 xff0c 动态设置select标签是否可以选择 xff0c 因此 xff0c 当判断某个select应当设为只读时 xff0c 使用 span class hljs variable span
  • java:程序包XXXX不存在

    使用idea导入maven项目 xff0c 编译时报错 xff1a java 程序包XXXX不存在 如图 xff1a 百度到的诸如右键libraries所在文件夹 xff0c 选择add to libraries 等方法没有作用 后来去查看
  • tomcat启动报错:java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.Lifec

    tomcat启动报错 xff1a java lang IllegalStateException ContainerBase addChild start org apache catalina Lifec 百度的结果一般都是让修改web
  • UE4 音乐的播放与停止--基于蓝图

    要实现的功能非常简单 xff1a 点击按钮 xff0c 播放音乐 这个功能非常基础 xff0c 就两步 xff1a 1 将音乐源文件拖到context文件夹中 注意 xff0c 这里的音乐文件必须是 wav格式的 2 在按钮的onclick
  • UnityEditor.BuildPlayerWindow+BuildMethodException

    unity3D安卓打包报错 xff1a UnityEditor BuildPlayerWindow 43 BuildMethodException 61 errors at UnityEditor BuildPlayerWindow 43
  • AI 入门怎么学?这份学习指南请收好!

    万事开头难 xff01 AI 入门对很多初学 AI 的同学来说是一大难题 搜集了一大堆入门资料 xff0c Python 数学 深度学习应有尽有 xff0c 但就是无从下手 xff0c 总是在第一章与放弃之间徘徊 那么 xff0c AI 应
  • 为什么越厉害的大厂,校招越不看重考试成绩?

    前几天赵同学告诉我 xff0c 他没有通过那家心仪的公司笔试 赵同学成绩不错 xff0c 每次都是专业前五 xff0c 但笔试中有一道 银行家算法实现 题 xff0c 他一点也没写出来 这就是大厂招聘不看重成绩单的原因 xff1a 招人是为
  • 我的2011——毕业之年的总结与彷徨

    题记 眼看2011即将成为过去 xff0c 难得在这最后的时刻 xff0c 抽点时间 xff0c 倒上一杯热茶 xff0c 回忆这一年的浮浮沉沉 这一年 xff0c 我和所有毕业生一样 xff0c 离开了呆了四年的大学校园 呆腻了校园的生活
  • centos安装anaconda教程

    1 更新yum 命令 xff1a sudo yum update 2 安装anaconda 2 1 查看anaconda对应python版本 我选的3 8版 Old package lists Anaconda documentation
  • Android布局 -- Navigation实现底部导航栏

    底部导航栏加页卡的切换 xff0c 很多App采用这种布局设计 xff0c 在以前的开发中 xff0c 需要自定义底部导航栏以及使用FragmentTransaction来管理Fragment的切换 xff0c 代码量较大 xff0c 而使
  • ViewModelProviders is deprecated

    原有的创建ViewModel的方法 xff1a viewModel 61 ViewModelProviders of this get ViewModel class 提示ViewModelProviders过时 改为 xff1a view
  • Android Fragment退出 返回上一个Fragment与直接退出

    例如应用底部有两个导航按钮A与B xff0c 刚进入的时候显示为第一个AFragment xff0c 点击B切换到BFragment 如果需求是在BFragment点击返回键回到AFragment xff0c 需要配置 app defaul
  • Android基础 -- 子线程可以修改UI吗?

    子线程可以修改UI吗 xff1f 为什么会产生这样的问题 xff0c 可能是因为在开发过程中遇到了 34 Only the original thread that created a view hierarchy can touch it