【Android 学习】深入理解Handler机制

版权声明:本文为博主原创文章,转载请注明出处http://blog.csdn.net/u013132758。 https://blog.csdn.net/u013132758/article/details/51355051

Android 提供了Handler和Looper来来满足线程间的通信,而前面我们所说的IPC指的是进程间的通信。这是两个完全不同的概念。大家不要搞混了。

Handler先进先出原则,Looper类用来管理特定线程内消息的交换(MessageExchange);

我们了解Handler之前首先要知晓一下几点:

  1. Looper:一个线程有一个Looper,由Looper来管理当前线程的MessageQueue(消息队列),就是无限将消息队列里的取出,一个Handler必须绑定一个Looper。
  2. Handler:Handler的主要作用是将某一任务切换到特定的线程来执行。为什么要有这个机制昵?接下来会为大家介绍:
  3. MessageQueueu:消息队列用来存放线程发出的消息。
  4. Tread:线程通常是指UI线程也就是主线程,每个线程创建时都会为其创建MessageQueue。

1、为什么会有Handler机制

我们刚说Handler机制的主要作用是将某一任务切换到特定的线程来执行,我们做项目可能都遇到过ANR(Application Not Response),这就是因为执行某项任务的时间太长而导致程序无法响应。这种情况我们就需要将这项耗时较长的任务移到子线程来执行,从而消除ANR。而我们都知道Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,那么程序就会抛出异常。而Android提供Handler就是为了解决在子线程中无法访问UI的矛盾。

2、demo

我们来先看一个简单的Demo吧!

MainActivity.java

package com.example.terry.hanlderdemo;import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.TextView;
/*** Demo描述:** 示例步骤如下:* 1 子线程给子线程本身发送消息* 2 收到1的消息后,子线程给主线程发送消息* 3 收到2的消息后,主线程给子线程发送消息** 为实现子线程给自己本身发送消息,关键还是在于构造Handler时传入的Looper.* 在此就传入该子线程自己的Looper即调用Looper.myLooper(),代码如下:* Looper.prepare();* mHandlerTest1=new HandlerTest1(Looper.myLooper());* Looper.loop();** 所以当mHandlerTest1.sendMessage(message);发送消息时* 当然是发送到了它自己的消息队列.** 当子线程中收到自己发送的消息后,可继续发送消息到主线程.此时只要注意构造* Handler时传入的Handler是主线程的Handler即可,即getMainLooper().* 其余没啥可说的.*** 在主线程处理消息后再发消息到子线程*** 其实这些线程间发送消息,没有什么;关键还是在于构造Handler时传入谁的Looper.**/
public class MainActivity extends Activity {private TextView mTextView;private HandlerTest1 mHandlerTest1;private HandlerTest2 mHandlerTest2;private int counter=0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();}private void init() {mTextView = (TextView) findViewById(R.id.textView);//1 子线程发送消息给本身new Thread() {public void run() {Looper.prepare();mHandlerTest1=new HandlerTest1(Looper.myLooper());Message message = new Message();message.obj = "子线程发送的消息Hi~Hi";mHandlerTest1.sendMessage(message);Looper.loop();}}.start();}private class HandlerTest1 extends Handler {private HandlerTest1(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);System.out.println("子线程收到:" + msg.obj);//2  收到消息后可再发消息到主线程mHandlerTest2=new HandlerTest2(getMainLooper());Message message = new Message();message.obj = "O(∩_∩)O";mHandlerTest2.sendMessage(message);}}private class HandlerTest2 extends Handler {private HandlerTest2(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);mTextView.setText("在主线程中,收到子线程发来消息:" + msg.obj);//3  收到消息后再发消息到子线程if (counter==0) {Message message = new Message();message.obj = "主线程发送的消息Xi~Xi";mHandlerTest1.sendMessage(message);counter++;}}}}


3、Android 消息机制分析

3.1 TreadLocal的工作原理

  1. TreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后只有在特定的线程中可以获取到存储的数据,对于其他线程来说则无法获取到。ThreadLocal用一句大白话来讲解,就是看上去只new了一份,但在每个不同的线程中却可以拥有不同数据副本的神奇类。其本质是ThreadLocal中的Values类维护了一个Object[],而每个Thread类中有一个ThreadLocal.Values成员,当调用ThreadLocal的set方法时,其实是根据一定规则把这个线程中对应的ThreadLocal值塞进了Values的Object[]数组中的某个index里。这个index总是为ThreadLocal的reference字段所标识的对象的下一个位置。

3.2  MessageQueue的工作原理

MessageQueue的工作原理:主要方法为enqueueMessage和next。 
 a. enqueueMessag主要就是一个单链表的插入操作, 
 b. next方法是一个无限循环,如果消息队列中没有消息,next方法就阻塞,有新消息到来时,next方法会返回这条消息并将其从单链表中删除。

3.3  Looper的工作原理: 

a. prepare方法,为当前没有Looper的线程创建Looper。
b. prepareMainLooper和getMainLooper方法用于创建和获取ActivityThread的Looper。
c. quit和quitSafely方法,前者立即退出,后者只是设定一个标记,当消息队列中的所有消息处理完毕后会才安全退出。子线程中创建的Looper建议不需要的时候都要手动终止。
d. loop方法,死循环,阻塞获取msg并丢给msg.target.dispatchMessage方法去处理,这里的target就是handler。

3.4Handler的工作原理:

a. 无论sendMessage还是post最终都是调用的sendMessageAtTime方法。
b. 发送消息其实就是把一条消息通过MessageQueue的enqueueMessage方法加入消息队列,Looper收到消息就会调用handler的dispatchMessage方法。它的处理过程参考流程图,一看就懂~ 


c. 这里我补充一个东西,当我们直接Handler h = new Handler()时,本质调用的是Handler(Callback callback, Boolean async)构造方法,这个方法里会调用Looper.myLooper()方法,这个方法其实就是返回的ThreadLocal里保存的当前线程的Looper,这也就解释了为什么我们在主线程中这样new没有问题,子线程中如果不先Looper.prepare会抛出异常的原因,前面多次说了,因为ActivityThread会在初始化的时候创建自己的Looper。

3.5主线程的消息循环:

这里写图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/284771.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【QGIS入门实战精品教程】4.3:QGIS属性表按字段链接外部属性数据

属性数据是GIS空格数据的重要组成部分。属性数据采集的基本操作由于地理实体(如建筑物) 位于地块之内成者与地块有关(如道路),因此,描述地理实体的属性数据和描述地块实体与地理实体之间关系的属性数强大多数都是土地信息的范畴土地空间数据库的属性教据主要是用来描述空间目…

解决 Cmder 的光标跟文字有个间距 及常用配置

具体的方法: 菜单 > SettingStartup > Environment set PATH%ConEmuBaseDir%\Scripts;%PATH% set LANGzh_CN.UTF8 chcp 65001 如果无效:在 Cmder 下的 verndor 目录里,修改 clink.lua 文件大约40和46行,把符号 λ 改为 # …

做一个高德地图的 iOS / Android .NET MAUI 控件系列 - 创建控件

我们知道 MAUI 是开发跨平台应用的解决方案 ,用 C# 可以直接把 iOS , Android , Windows , macOS , Linux ,Tizen 等应用开发出来。那我们在这个框架除了用底层自定义的 UI 控件外,如果我们要用如高德地图这样的第三方控件,要如何做呢&#x…

flask中的session,render_template()第二和参数是字典

1. 设置一个secret_key 2.验证登入后加上session,这是最简单,不保险 . 3.注意render_template传的参数是字典 转载于:https://www.cnblogs.com/cuzz/p/8087844.html

失败记录两则

一,未找出为什么有的CPU应用超高,而另一些CPU静静啥也不干。可能是将JOB的优先级设置低了? 二,给BOSS的三星I9300线刷港版ROM失败。可能文件坏,最可能数据线不是原装?

【QGIS入门实战精品教程】1.1:QGIS与GIS的区别和联系

「刘一哥GIS」系列专栏《QGIS入门实战精品教程(配套案例数据)》目录 1. QGIS概述 QGIS(在2.0版本之前称之为Quantum GIS)于2002年由Gary Sherman创立,在2007年由OSGeo接管,并于2009年发布了1.0版本,目前最高版本为3.22。QGIS采用开源证书GNU GPLv2 (GNU General Public…

简单的对拍

在算法竞赛中,我们常用对拍来初步检验程序。 网上也有其它的关于对拍的教程,但是任性的我还是要自己写一篇教程。  首先,我们要知道我们是用一个叫做” 批处理文件(.bat)“的东西来处理这个问题。点击初步了解bat 不过…

AI 之 OpenCvSharp 大图找小图(案例版)

要说跟AI扯上啥关系,估计只有库本身了,但是,这个大图搜小图功能还是不错的,有喜欢的可以试试。我主要用的场景是,具体的某个界面为大图,界面的某个图标为小图,或者,整个桌面为大图&a…

SaaS应用的十二要素

简介 如今,软件通常会作为一种服务来交付,它们被称为网络应用程序,或软件即服务(SaaS)。12-Factor 为构建如下的 SaaS 应用提供了方法论: 使用标准化流程自动配置,从而使新的开发者花费最少的…

路由器与交换机区别

路由器与交换机区别 近日,在回答知道网友提问的时候,发现很多朋友不知道路由器与交换机的不同,常常问一些看起来有点可笑的问题。比如路由器同时登陆两个账号,所以今天写下这篇经验,科普一下路由器的功能和交换机的功能…

【QGIS入门实战精品教程】1.2:QGIS与ArcGIS的区别和联系

「刘一哥GIS」系列专栏《QGIS入门实战精品教程(配套案例数据)》目录 以下是GISGeography官方给出的两者之间的27点比较(译): 官方网址:https://gisgeography.com/qgis-arcgis-differences/ 1.QGIS容纳更多的数据格式 ArcGIS没有支持所有的数据格式,毫无疑问,QGIS在处…

押注.NET 是件好事

作者 | Bryan Costanich译者 | 明知山策划 | 褚杏娟作为一个在.NET 上构建了不止一个流行平台的人,我经常被问到它的相关性,以及它是不是一个值得投入的生态系统。这个问题在旧金山湾区的技术世界里尤为流行,这里的技术潮流就像四季一样变更交…

8-12 canvas专题-阶段练习一(上)

8-12 canvas专题-阶段练习一&#xff08;上&#xff09; 1 <!DOCTYPE html>2 <html lang"zh-cn">3 <head>4 <meta charset"UTF-8">5 <title>8-12 课堂演示</title>6 </head>7 <style type"text…

【QGIS入门实战精品教程】2.2:QGIS软件的下载与安装(Windows)

文章目录 一、QGIS下载二、QGIS安装​一、QGIS下载 QGIS软件官网下载地址:https://www.qgis.org/en/site/ 进入官网之后,点击【Download Now】进入下载页面: 选择对应的版本进行下载: 开始下载:

Linux sudo找不到命令:修改sudo的PATH路径

为什么80%的码农都做不了架构师&#xff1f;>>> sudo有时候会出现找不到命令&#xff0c;而明明PATH路径下包含该命令&#xff0c;让人疑惑。其实出现这种情况的原因&#xff0c;主要是因为当 sudo以管理权限执行命令的时候&#xff0c;linux将PATH环境变量进行了重…

快速判断站点是否存活的 3 种编程实现

前言如何知道外部站点是否正在运行&#xff1f;如果使用浏览器&#xff0c;直接访问对应站点即可。那么&#xff0c;使用 C# 以编程方式&#xff0c;如何实现呢&#xff1f;GET 请求首先想到的&#xff0c;就是使用 HttpClient 向该站点发送 HTTP 请求&#xff0c;并检查返回状…

【数据结构与算法】拓扑排序问题C语言实现

拓扑排序是有向无环图的一种应用,在实际生活中用的很多。 比如GIS专业的课程设计,许多课程需要前置课程要求,也就是说没上过A课程、则不可能直接去学B课程,画个图表就是: 同理,我们教材中的范例:穿衣服的过程也是一个拓扑排序问题,如下表: 有关这个拓扑排序的模型构造…

[转]2020年排名前20的基于SpringBoot搭建的开源项目,帮你快速进行项目搭建!

△Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第 287 篇原创分享 作者 l Hollis 来源 l Hollis&#xff08;ID&#xff1a;hollischuang&#xff09; SpringBoot一直是开发者比较青睐的一款轻量级框架&#xff0c;他不仅继承了Spring框架原有的优秀特性&#xff0c;而…

C# 搭建一个基于.NET5的WPF入门项目

概述.NET5 发布已经有一阵子了&#xff0c;今天抽空体验一哈&#xff0c;搭建一个WPF项目实例&#xff0c;看看和传统的.NET Framework有什么区别&#xff01;开发环境&#xff1a;VS2019 WPF框架&#xff1a;Caliburn.Micro 版本4.0.173.NET版本&#xff1a;.NET5.0项目创建步…

五个最佳案例带你解读 Node.js 的前后之道

Node.js 是什么&#xff1f; Node.js 采用 C语言编写而成&#xff0c;浏览器内核 V8 做为执行引擎&#xff1b; Node 不是 JS 应用、而是一个 Javascript 的运行环境。 Node 保留了前端浏览器 js 的接口&#xff0c;没有改写语言本身的任何特性&#xff0c;依旧基于作用域和原型…