| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> Handler那些事儿(1) -> 正文阅读 |
|
[移动开发]Handler那些事儿(1) |
文章转载自第一代码原创文章:https://www.diyidaima.com/android/126.html 1、一个线程有几个Handler?你在MainActivity new一个Handler,在LoginActivity也可以new 一个Handler,甚至在Fragment也可以new一个Handler,而这些线程均是主线程,所以一个线程可以有多个Handler; 2、一个线程有几个Looper,如何保证?一个线程只有一个Looper。那如何保证一个线程只有一个Looper呢?首先通过下面的Looper源码,我们看到Looper是通过调用静态方法prepare()进行初始化的,而prepare()又调用了prepare(true)方法。
那Looper是如何和线程Thread关联上的呢?查看下方ThreadLocal的部分源码,上方sThreadLocal.set(…)方法内部通过Thread.currentThread()获取到当前的线程currentThread,也就是给当前线程加入了指向Looper的变量,然后通过values(currentThread)获取到Thread的变量localValues,其类型为ThreadLocal.Values,然后再赋值给values,如果为null,则进行初始化,最后将ThreadLocal和Looper put进values里。这样当前线程就和Looper关联到了一起。 那如何保证唯一性呢? 我们再翻看源码,在Looper源码里,只有一个地方调用了sThreadLocal.set(…),这也保证了Looper被初始化的唯一性。 3.Handler内存泄露原因?为什么其他的内部类没有说有过这个问题?首先是因为内部类持有外部类的引用,导致即使Activity等执行了onDestory()也不会释放,因为jvm识别到该activity被引用了,所以不会对其进行回收,导致内存泄露。那为什么其他的内部类没有说有过这个问题呢?主要是Handler可以发送延时类消息,消息不到时间不会被销毁。同时MessageQueue持有Message,Message持有了Handler,而Handler持有了Activity,只有消息被销毁了,才会销毁Handler,Handler才会销毁Activity。所以Handler内部类持有外部类的引用才会发生内存泄露。那如何解决Handler的内存泄露问题呢? 4.为什么主线程可以new Handler()呢?如果想要在子线程中new Handler()需要做些什么准备?在ActivityThread(主线程)里有一个main()函数,它是每一个Android应用最早执行的函数。如下源码:
可以看到,ActivityThread(主线程)的main函数已经执行了Looper.prepareMainLooper()和Looper.loop()。我们再来看看Looper.prepareMainLooper()的源码:
我们看到,其内部使用prepare(false)初始化了一个Looper。所以说主线程一启动,在main()函数中,由系统已经帮我们完成执行prepare() 和 loop()了,所以可以直接new Handler(),不需要再进行Looper初始化了。并且主线程中的所有代码,全都运行在这两个函数(prepareMainLooper()/prepare() 和 loop())之间。所有的线程都必须要prepare()和loop(),如果子线程中想要进行Handler操作,就必须在子线程中执行prepare() 和 loop()。 5.子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?当MessageQueue没有Message时,loop()会阻塞状态,会一直卡在Message msg = queue.next()里。那此时我们需要调用Looper的quitSafely()或quit()函数,其内部均会调用MessageQueue的quit()把消息队列中的全部消息给移除掉,这样就释放了内存。而在quit()函数的最后一行,会执行一个nativeWake()函数,唤醒queue.next()里的nativePollOnce(ptr, nextPollTimeoutMillis),唤醒后,才能往下执行,发现 Message msg = mMessages是空的,就会执行dispose()和return null,此时Looper就跳出了死循环,释放线程。综上所述,子线程中维护的Looper,消息队列无消息的时,可以调用Looper的quitSafely()或quit()函数,使得Looper结束死循环,并且起到释放内存和线程的作用。 6.既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程中),那它内部是如何确保线程安全的?在往MessageQueue添加数据时,使用syncchronized线程锁,在往MessageQueue取数据时,也使用syncchronized线程锁,保证消息不会混乱。 7.我们使用Message时应该如何创建它?Message使用了享元设计模式,在销毁时并没有真正的销毁,只是将内部变量置为初始值或者说清零,在创建的时候,使用Message.obtain()进行创建,其内部会从队列头部取出Message对象复用。 8.使用Handler的postDelay后消息队列会有什么变化?使用Handler的postDelayed(@NonNull Runnable r, long delayMillis)发送延时消息,该postDelayed内部会调用sendMessageDelayed(@NonNull Message msg, long delayMillis)方法,sendMessageDelayed又调用MessageQueue的enqueueMessage方法。如果MessageQueue为空,此时调用enqueueMessage会通过内部的nativeWake方法唤醒消息队列,消息队列被唤醒后,next方法会被执行,从而触发计算该Message执行需要等待的时间,计算完之后重新等待,直到时间到了才会触发相应操作。 9. Looper死循环为什么不会导致应用卡死?我们先看主线程ActivityThread的main方法的源码:
我们在Launch桌面点击app的图标时,会先向AMS请求启动根Activity,AMS又会向zygote请求启动应用程序进程socket,zygote会创建并启动应用程序进程,应用程序进程准备就绪后返回给AMS,AMS再启动根Activity。最终进入到ActivityThread的main方法,在main方法里面创建Looper和MessageQueue处理主线程的消息,然后Looper.loop()方法进入死循环,我们的Activity的生命周期都是通过Handler机制处理的,包括 onCreate、onResume等方法。下面是loop方法循环:
通过创建一个for死循环来处理消息 msg.target.dispatchMessage(msg); 真正卡死主线程操作的是在回调方法onCreate、onStart、onResume等操作时间过长,会导致掉帧甚至ANR,Looper.loop()本身不会导致应用卡死。主线程的死循环一直运行不会特别消耗CPU资源在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生。所以说主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。 |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 7:54:51- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |