Android中的HandlerThread分析_android 怎么判断handlerthread中的线程有没有执行完-程序员宅基地

技术标签: handler  HandlerThread  android  thread  Android筑基  

HandlerThread简介

HandlerThread,顾名思义,是一个在其内部可以使用Handler的线程,其实本质是HandlerThread线程内部构造了一个Looper环境。源码如下:在其run方法中初始化了一个Looper的环境,创建了Looper对象并且开启了loop循环。

//HandlerThread run()方法
@Override
    public void run() {
    
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
    
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

注意Looper对象的创建与Looper对象的获取使用了同步锁,以及线程的等待唤醒机制,如果HandlerThread线程Looper对象尚未初始化完毕,则外部获取Looper的线程将处于等待状态,直到Looper初始化完成,然后唤醒所有的等待线程,源码如下

//HandlerThread getLooper()方法
 public Looper getLooper() {
    
        if (!isAlive()) {
    
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
    
            while (isAlive() && mLooper == null) {
    
                try {
    
                    wait();
                } catch (InterruptedException e) {
    
                }
            }
        }
        return mLooper;
    }

因为HandlerThread内部开启了Looper loop方法进行了死循环,所以该线程会一直处于运行状态,不会销毁。所以该线程使用完毕之后我们应该主动调用quit()或者quitSafely()方法退出Looper循环,进而结束线程。

//HandlerThread quit()方法
public boolean quit() {
    
        Looper looper = getLooper();
        if (looper != null) {
    
            looper.quit();
            return true;
        }
        return false;
    }
//HandlerThread quitSafely()方法
public boolean quitSafely() {
    
        Looper looper = getLooper();
        if (looper != null) {
    
            looper.quitSafely();
            return true;
        }
        return false;
    }

Looper quit()和quitSafely()区别

  • quit(),调用后直接终止Looper,不在处理任何Message,所有尝试把Message放进消息队列的操作都会失败,比如Handler.sendMessage()会返回false,但是存在不安全性,因为有可能有Message还在消息队列中没来的及处理就终止Looper了。
  • quitSafely(),调用后会在所有消息都处理后再终止Looper,所有尝试把Message放进消息队列的操作也都会失败。
Looper退出的原理分析

如下源码所示,loop()里面有一个for循环,只有当MessageQueue的next()返回null的时候才会退出循环终止Handler机制。再看看MessageQueue的next()我们也看到一个for循环,如果有消息的话就把消息返回,没有消息且mQuitting=false的时候继续循环下去,只有当没有消息然后mQuitting=true的时候返回null。根据Looper代码所示Looper.quit()最终会调用MessageQueue.removeAllMessagesLocked()表示直接把消息队列里面的消息清空,而Looper.quitsafely()会调用MessageQueue.removeAllFutureMessagesLocked(),表示把所有延迟消息清除。

//Looper loop()方法
public static void loop() {
    
        final Looper me = myLooper();
        if (me == null) {
    
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        ...
        for (;;) {
    
            Message msg = queue.next(); // might block
            //当msg==null时,会退出loop循环
            if (msg == null) {
    
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
    
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            msg.target.dispatchMessage(msg);

            if (logging != null) {
    
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            ...
            msg.recycleUnchecked();
        }
    }
//MessageQueue next()方法
Message next() {
    
        ...
        for (;;) {
    
            ...
            synchronized (this) {
    
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
    
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
    
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                //有消息
                if (msg != null) {
    
                    if (now < msg.when) {
    
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
    
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
    
                            prevMsg.next = msg.next;
                        } else {
    
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
    
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                //没有消息并且mQuitting==true,则return null
                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
    
                    dispose();
                    return null;
                }
                ...
            }
        }
    }
//Looper quit()
public void quit() {
    
        mQueue.quit(false);
    }
    
//Looper quitSafely()
public void quitSafely() {
    
        mQueue.quit(true);
    }
    
//MqssageQueue quit(bool safe)方法
void quit(boolean safe) {
    
        if (!mQuitAllowed) {
    
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
    
            if (mQuitting) {
    
                return;
            }
            mQuitting = true;

            if (safe) {
    
                //quit()
                removeAllFutureMessagesLocked();
            } else {
    
                //quitSafely()
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

可以总结到,quit()实际上是把消息队列全部清空,然后让MessageQueue.next()返回null,令Looper.loop()循环结束从而终止Handler机制,但是存在着不安全的地方是可能有些消息在消息队列没来得及处理。而quitsafely()做了优化,只清除消息队列中延迟信息,等待消息队列剩余信息处理完之后再终止Looper循环。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lilinjie_blog/article/details/106850858

智能推荐

基于linux服务器的hashcat握手包破解_hashcat 22000-程序员宅基地

文章浏览阅读7.5k次,点赞14次,收藏47次。目录前言:为什么要使用linux服务器?一、握手包的抓取二、握手包格式转换(cap to hc22000)三、linux服务器的环境配置四、利用hashcat进行暴力测试总结前言:为什么要使用linux服务器?当下对于wifi密码的破解,主流的两种手段是握手包破解和WPS破解,而WPS破解却仅限于无线路由器。如果你到了一条陌生的街道或者办公场所,大部分情况下,手机热点是会比无线路由器信号要多的。所以握手包破解还是更加普适。hashcat作为最快的密码恢复工具之一,能够支持GPU加速计算,相关的测试表明_hashcat 22000

什么是人工智能?(科普)_人工智能科普-程序员宅基地

文章浏览阅读5.8k次,点赞8次,收藏56次。  【百度百科】  百度百科给出人工智能的概念及知识,专业科学,但一定也会让不少人望而生畏。  人工智能(Artificial Intelligence),英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。  人工智能是计算机科学的一个分支,它企图了解智能的实质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器,该领域的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。人工智能从诞生以来,理论和技术日益成熟,应用领域_人工智能科普

【资源搜集】7 款不错的聊天机器人 API 接口搜集_免费的chatai接口-程序员宅基地

文章浏览阅读2.6w次,点赞29次,收藏146次。前段时间玩微信爬虫,仿佛打开了新世界的大门。使用 wxpy 库调用微信的接口,可以获取好友信息,聊天消息收发等功能。脑洞一开,这样不就可以用来做 聊天消息防撤回,红包提醒,自动回复这些有意思的玩意儿了嘛!言归正传,这次我搜集整理了一些比较好用的 ”聊天机器人“ 的 API 接口。如果后期有机会的话,把它们接入我的程序中,做一个陪聊小助手,也是一个不错的选择。小 i 机..._免费的chatai接口

毕业设计:基于深度学习的网课学生坐姿检测系统-程序员宅基地

文章浏览阅读889次,点赞24次,收藏36次。毕业设计:基于深度学习的网课学生坐姿检测系统利用深度学习算法对摄像头捕捉的学生图像进行特征提取和分类,以判断学生的坐姿是否正确。首先,通过图像采集模块获取学生的实时视频流,判断学生的坐姿状态。为计算机毕业设计提供了一个创新的方向,为毕业生提供了一个有意义的研究课题。对于计算机专业、软件工程专业、人工智能专业、大数据专业的毕业生而言,提供了一个具有挑战性和创新性的研究课题。无论您对深度学习技术保持浓厚兴趣,还是希望探索机器学习、信息安全、算法或人工智能的领域的同学,能为您提供灵感和指导;_坐姿检测系统

include详解 shell_thinkphp诸多限制条件下如何getshell详解-程序员宅基地

文章浏览阅读300次。前言先说说2020_n1CTF的web题Easy_tp5复现问题。这个题在保留thinkphp的RCE点的同时,并且RCE中ban掉许多危险函数,只能允许单参数的函数执行。对于现在在网络中流传的文件包含的点也增加了限制。smile yyds!先说一下这个题限制条件:thinkphp版本:5.0.0php版本:7对于包含文件增加了限制ban掉所有的单参数危险函数设置open_basedir为web目..._thinkphp5.x getshell 禁用函数

【好文推荐】Gradle-源码全解析(1)_gradle源码分析-程序员宅基地

文章浏览阅读741次,点赞14次,收藏20次。希望本文对你有所启发,有任何面试上的建议也欢迎留言分享给大家。好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。好了~如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。_gradle源码分析

随便推点

python dataframe转array(as_matrix()替换方法 .values_as_matrix() 代替-程序员宅基地

文章浏览阅读1.6k次。使用as.matrix报错:_as_matrix() 代替

北邮计算机学院2017届复试经验分享_北邮计算机技术复试上机很重要吗-程序员宅基地

文章浏览阅读1w次,点赞3次,收藏35次。北邮计算机学院2017届复试经验分享 初试完了再来担心复试,有看复试经验的时间还不如多做两道数学题! 导师:了解导师的情况,最差也不要找一个人不好的老师,其次尽量选自己喜欢的方向,出成绩以后尽早联系导师,加一加群,不要信息闭塞,有学长学姐就更好了,礼貌的咨询他们。 考完试对一下答案,如果差不多能过就可以准备一下复试了,因为北邮的复试尤其是机试挺难的。 机试:很重要,..._北邮计算机技术复试上机很重要吗

Python第四周作业之选择题_下关于lambda函数说法错误的是:()-程序员宅基地

文章浏览阅读1.5w次,点赞11次,收藏76次。Python第四周作业之选择题1. 以下关于递归函数基例的说法错误的是:2. 以下选项不是函数作用的是:3. 以下关于Python函数说法错误的是:4. 以下关于模块化设计描述错误的是:5. 以下对递归描述错误的是:6. 以下关于函数说法错误的是:7. 哪个选项对于函数的定义是错误的?8. 函数定义时,以下不需要使用global声明就可能操作全局变量的类型是:9. 以下关于lambda函数说法错误的是:10. 以下能够返回struct_time类型时间的函数是:11. 哪个选项是下面代码的执行结果?12. _下关于lambda函数说法错误的是:()

Linux系统下安装jdk及环境配置-程序员宅基地

文章浏览阅读66次。Linux系统下安装jdk及环境配置1、利用yun 云安装 :首先在liunx下输入:yum -y list java*→yum install -y java-1.8.0-openjdk-devel.x86_64(jdk1.8版本)2、手动安装:去Oracle官网下载需要安装的jdk版本,我这里用的是jdk-8u181-linux-x64.tar.gz将该文件包放在user/java下...

C语言简单的数据结构:单链表的有关算法题(2)-程序员宅基地

文章浏览阅读983次,点赞26次,收藏6次。接着我们介绍后面的三道题,虽然代码变多了但我们的思路更加通顺了。题目:4. 单链表相关经典算法OJ题3:合并两个有序链表5. 循环链表经典应⽤-环形链表的约瑟夫问题6. 单链表相关经典算法OJ题5:分割链表

《Android App开发入门与项目实战》资源下载和内容勘误_android app开发入门与项目实战pdf-程序员宅基地

文章浏览阅读3.2k次。下面是《Android App开发入门与项目实战》一书用到的工具和代码资源:1、本书使用的Android Studio版本为4.1,最新的安装包可前往。2、本书提供所有示例源码的demo工程下载,源码(适配Android4.4到Android11)的下载方式见该书前言末尾的二维码,获取ppt课件同样扫描前言末尾的二维码。最新的源码也可访问我的gitee获取。_android app开发入门与项目实战pdf

推荐文章

热门文章

相关标签