Handler浅析

2024-08-29 07:58
文章标签 handler 浅析

本文主要是介绍Handler浅析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

Android中多线程经常会涉及到传递消息的问题,尤其是更新UI,众所周知,子线程是无法更新UI,这时候就要借助Handler的消息传递机制。


一、构成

  1. Message: 消息,Handler处理的对象
  2. Looper:每个线程都有自己的looper,他负责将消息分发给对应的Handler
  3. MessageQueue: 初始化Looper对象的时候,会生成MessageQueue,它管理着Message
  4. Handler: 发送和处理消息

二、实例

我们先来看一个实例:

public class MainActivity extends AppCompatActivity {MyThread mThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mThread = new MyThread();mThread.start();mThread.mHandler.sendEmptyMessage(0x01);}class MyThread extends Thread {public Handler mHandler;@Overridepublic void run() {//初始化Looper,并生成MessageQueueLooper.prepare();mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {//处理发送过来的消息}};//不断的循环获取消息,并分发消息,没有消息时推出循环Looper.loop();}}
}

三、源码分析

1.Looper

1.1 Loop.prepare()

    public static void prepare() {prepare(true);}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}//这个是ActivityThread类中使用的public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}

我们来解析private static void prepare(boolean quitAllowed),sThreadLocal是ThreadLocal类型,它有set和get方法

ThreadLocal:线程本地存储空间,不同线程之间,不能互相访问

ThreadLocal.set(T value):
将数据存储到ThreadLocal

    public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}

ThreadLocal.get():
从ThreadLocal中得到数据

    public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null)return (T)e.value;}return setInitialValue();}

1.2 Looper构造函数

    private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}

从代码可以看出,MessageQueue是在Looper生成的

1.3 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;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();for (;;) {//获取消息Message msg = queue.next(); // might block//如果没有消息,就会退出// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long traceTag = me.mTraceTag;if (traceTag != 0) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}try {//分发消息msg.target.dispatchMessage(msg);} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);}//将消息放入消息池msg.recycleUnchecked();}}

1.4 dispatchMessage

上面Looper.loop()中有一行这样的代码:

msg.target.dispatchMessage(msg);

它的作用是分发消息,我们来看看它的具体实现:

    /*** Handle system messages here.*/public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

从这方法可以很清楚的看到,调用顺序:


  1. 当msg的回调方法部位null,则回调msg.callback.run();
  2. 当Handler的mCallback成员变量不为null时,则回调方法mCallback.handleMessage(msg);
  3. 调用Handler的回调方法handleMessage(),该方法默认为空,Handler子类需要覆写该方法。

一般情况下我们用的是第三种方法,也就是Handler.handleMessage()

2.消息

发送消息有很多方法,我例举两个:post和sendEmptyMessage,不过他们最终调用的是MessageQueue.enqueueMessage()。

post:

    public final boolean post(Runnable r){return  sendMessageDelayed(getPostMessage(r), 0);}

sendEmptyMessage:

    public final boolean sendEmptyMessage(int what){return sendEmptyMessageDelayed(what, 0);}

enqueueMessage:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}

post方法里面有有一个Runnable,但是这并不是新开了一个线程,而是把消息加Handler所在线程的消息队列里

3.Handler

handler的构造函数为:
无参:

    public Handler(Callback callback, boolean async) {if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}//必须先执行Looper.prepare(),不然mLooper会等于nullmLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}

Looper为当前线程的Looper

有参:

    public Handler(Looper looper, Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;}

可以指定Looper

4. MessageQueue

4.1 enqueueMessage

添加一条消息到消息队列,源码省略

4.2 removeMessages

移除制定条件的消息,源码省略

5. 消息池

类似于线程池,当消息池不为空时,直接从消息池中获取Message对象,而不是直接创建Message,提高效率。

消息池有两个重要的方法,分别为:obtain()和recycle()

5.1 obtain()

    public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();}

当sPool不为空,直接从头部获取一个消息,如果sPool为空,则重新创建一个消息

5.2 recycle()

    public void recycle() {if (isInUse()) {if (gCheckRecycle) {throw new IllegalStateException("This message cannot be recycled because it "+ "is still in use.");}return;}recycleUnchecked();}//将不再使用的消息加入消息池void recycleUnchecked() {// Mark the message as in use while it remains in the recycled object pool.// Clear out all other details.flags = FLAG_IN_USE;what = 0;arg1 = 0;arg2 = 0;obj = null;replyTo = null;sendingUid = -1;when = 0;target = null;callback = null;data = null;synchronized (sPoolSync) {if (sPoolSize < MAX_POOL_SIZE) {next = sPool;sPool = this;sPoolSize++;}}}

这篇关于Handler浅析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1117297

相关文章

浅析CSS 中z - index属性的作用及在什么情况下会失效

《浅析CSS中z-index属性的作用及在什么情况下会失效》z-index属性用于控制元素的堆叠顺序,值越大,元素越显示在上层,它需要元素具有定位属性(如relative、absolute、fi... 目录1. z-index 属性的作用2. z-index 失效的情况2.1 元素没有定位属性2.2 元素处

浅析Python中的绝对导入与相对导入

《浅析Python中的绝对导入与相对导入》这篇文章主要为大家详细介绍了Python中的绝对导入与相对导入的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1 Imports快速介绍2 import语句的语法2.1 基本使用2.2 导入声明的样式3 绝对import和相对i

浅析如何使用Swagger生成带权限控制的API文档

《浅析如何使用Swagger生成带权限控制的API文档》当涉及到权限控制时,如何生成既安全又详细的API文档就成了一个关键问题,所以这篇文章小编就来和大家好好聊聊如何用Swagger来生成带有... 目录准备工作配置 Swagger权限控制给 API 加上权限注解查看文档注意事项在咱们的开发工作里,API

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

(入门篇)JavaScript 网页设计案例浅析-简单的交互式图片轮播

网页设计已经成为了每个前端开发者的必备技能,而 JavaScript 作为前端三大基础之一,更是为网页赋予了互动性和动态效果。本篇文章将通过一个简单的 JavaScript 案例,带你了解网页设计中的一些常见技巧和技术原理。今天就说一说一个常见的图片轮播效果。相信大家在各类电商网站、个人博客或者展示页面中,都看到过这种轮播图。它的核心功能是展示多张图片,并且用户可以通过点击按钮,左右切换图片。

风暴项目个性化推荐系统浅析

风暴项目的主要任务是搭建自媒体平台,作为主开发人员的我希望把工作重心放在个性化推荐系统上。 目前风暴项目的个性化推荐是基于用户行为信息记录实现的,也就是说对于每条资讯,数据库中有字段标明其类型。建立一张用户浏览表,对用户的浏览行为进行记录,从中可以获取当前用户对哪类资讯感兴趣。 若用户第一次登陆,则按默认规则选取热点资讯做推荐,及所有资讯按浏览量降序排序,取前4个。另外,我考虑到后期可能有商业

webservice的安全机制2---handler实现

本节摘要:本节介绍使用handler的方式来实现webservice的IP地址的校验。   1.引言 前一节介绍了使用users.lst文件来实现webservice的用户名和密码的校验, 本节介绍使用webservice的handler来实现webservice的安全校验。 这里,不用用户名和密码来实现安全校验,换一种方式,采用IP地址校验的方式。 这里通过一个配置文件来控制是否打开

中国书法——孙溟㠭浅析碑帖《越州石氏帖》

孙溟㠭浅析碑帖《越州石氏帖》 《越州石氏帖》  是一部汇集多本摹刻的帖,南宋时期的会稽石邦哲(字熙明)把家藏的一些法书碑帖集中一起摹刻成的,宋理宗时临安书商陈思《宝刻丛编》有记載这部帖的目录。现在还存有宋代时拓的残缺本,大多是相传的晋朝唐朝的小楷,后人多有临摹学习,并以此版本重新摹刻。 (图片来源于网络) 图文/氿波整理

浅析网页不安装插件播放RTSP/FLV视频的方法

早期很多摄像头视频流使用的是RTSP、RTMP协议,播放这类协议的视频通常是在网页上安装插件。但现在越来越多的用户,对于网页安装插件比较反感,且随着移动设备的普及,用户更多的希望使用手机、平板等移动设备,直接可以查看这些协议的视频。那是否有什么方案可以直接网页打开RTSP、RTMP协议的视频,直接观看不用安装插件呢?而且对于摄像头的数据,尽可能低延迟的获取实时画面。  其实很多摄像头厂家也注意到