深入理解Handler(一) --- Android中为什么非UI线程不能更新UI

一、前言

通过这篇文章带大家了解以下内容

  • 是否能够理解UI线程的工作机制
  • 是否熟悉SurfaceView实现高帧率的原理

二、为什么非UI线程不能更新UI

这个问题我们要从以下几个方面来探讨

  • UI线程的工作机制
  • 为什么UI设计成线程不安全的
  • 非UI线程一定不能更新UI吗

2.1 UI线程是什么怎么来的

在这里插入图片描述

我们知道所有的App进程通过 Zygote fork出来,当我们的App进程启动后会执行 ActivityThreadmain函数,在main函数里会调用Looper对象调用loop函数

   public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // Install selective syscall interception
        AndroidOs.install();
        CloseGuard.setEnabled(false);
        Environment.initForCurrentUser();
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
        Process.setArgV0("<pre-initialized>");
        Looper.prepareMainLooper();

        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Looper.loop()进入循环中,所以looper是不会退出的,否则就会抛出异常。这里我们要捋一捋概念,
在Android中,通常来说:

  • 主线程 = UI线程
  • 其他线程 = 非UI线程

小结一下,UI线程其实就是主线程的别称,他在应用启动时,在ActivityThread执行main函数被创建。紧接着会调用 Looperloop进入死循环。

这里留一个问题,我们知道Activity的主线程堵塞超过5s会触发ANR,为什么Looper中的死循环不会出发ANR呢?自后续的文章会为大家揭晓

2.2 主线程消息处理是如何工作

在这里插入图片描述

这里简述下整个消息处理工作流程

  • Handler通过sendMessage 发送Message到MessageQueue队列
  • Looper通过loop(),不断提取出到达触发条件的Message,并将message交给target来处理
  • 经过dispatchMessage后,交回给handler的handlerMessage来进行相应的处理
  • 将message加入MessageQueue时,往管道写入字符,可以唤醒loop线程;如果MessageQueue中没有Message,并处于idle状态,则会执行idelHandler接口中的方法,往往用于做一些请理性地工作。

2.3 UI为什么不设计成线程安全的

  • UI具有可变性,甚至是高频可变性

UI刷新比如游戏可能需要达到60帧,这样频繁的加锁十分影响性能

  • UI对响应时间的敏感性要求UI操作必须高效
  • UI组件必须批量绘制来保证效率

所以,UI不考虑设计成线程安全的

2.4 非UI线程一定不能更新UI吗

在这里插入图片描述

大多数情况下,我们逻辑的运行,比如网络IO,本地文件IO都处理在非UI线程中,为了提高UI线程的响应时间和响应速度

SurfaceView

在SurfaceView中在绘制时,通过加锁和解锁来绘制,不一定需要在UI线程(这里指的Android AcitivtyThread运行的主线)中刷新,他也在非UI线程绘制,这样整个帧率会提高很多,并且UI线程压力会小很多,比如游戏或者地图等等,肯能是`SurfaceView `或者`TextureView `,应用场景在 视频播放,地图显示,或者相机预览等等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值