首页 > 学院 > 开发设计 > 正文

Handler学习笔记

2019-11-06 09:39:05
字体:
来源:转载
供稿:网友

Handler是什么?

Handler的源码中有一段英文解释:

A Handler allows you to send and PRocess {@link Message} and Runnable objects associated with a thread’s {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

翻译过来就是:

Handler允许你发送和处理与这个线程消息队列相关联的消息和可运行对象。每一个Handler实例都与一个单独的线程和这个线程的消息队列有关。当你创建一个新的Handler,它将与这个线程创建的消息队列绑定,从那时开始,它将可以传递消息和可执行对象给消息队列,并在消息出队列时执行它们。

为什么要使用Handler?

Handler主要用于:

调度消息(Messages)或者可执行对象(Runnables)使其在将来某一时刻执行。将一些操作放在另一个线程中进行,线程中通信。 我觉得第二点主要提供子线程更新UI的机制

注:只有主线程可以直接操作UI,子线程通过Handler机制更新UI,主要是为了解决多线程并发造成的问题(多线程同时操作UI,容易界面错乱,若加锁会导致性能下降)。

如何使用Handler?

handler的简单使用步骤:

在主线程创建Handler对象handler子线程通过handler发送消息到handler的消息队列,发送消息可以使用多种方法,post, sendMessage等,本质上都是调用了sendMessageAtTime(Message msg, long uptimeMillis)handler线程的Looper不断循环队列,获取消息,并通过handleMessage()处理。

Handler原理解析

Handler的创建

Handler有许多重载的构造方法,最终调用为:

Handler(Callback callback, boolean async)Handler(Looper looper, Callback callback, boolean async)

其中上一个方法是使用当前线程的Looper对象,后一个使用提供的Looper对象代替默认的对象,@callback 指处理消息的回调接口, @async 是否异步。关于是否异步我还没有研究,这篇文章讲解的很具体。 下面具体看一下Handler()方法

public Handler() { this(null, false);}

其实调用了Handler(Callback callback, boolean async)

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()); } } mLooper = 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.myLooper()获取mLooper对象,判空后,设置该handler的mQueue为mLooper.mQueue。

public static @Nullable Looper myLooper() { return sThreadLocal.get();}

myLooper()方法获取当前线程的相关的looper对象,这个对象是唯一的,与线程相关,looper的设置在prepare()方法中。

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));}

所以说handler的创建需要在Looper.prepare()之后。

创建handler的时候mQueue设置为mLooper.mQueue,mQueue是在何时初始化的呢?

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

在Looper初始化的时候就已经初始化了MessageQueue即mQueue。

Handler发送消息

handler有一系列发送消息的方法,这些方法最终的调用关系如下图所示: handler发送方法的调用层次 图片来源:http://www.jb51.net/article/76483.htm 作者的思路非常清晰,在此借鉴一下。

enqueueMessage()方法如下:

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

无论是message还是runnable最后都转化成了message, 执行enqueueMessage时首先将msg.target设置为this,即调用的handle本身,然后设置消息的同步异步,最后放入消息队列中。设置target的目的应该是Looper循环是可以让正确的handler接收。

构造发送的Message对象也有两种方式:

新建 一个Message对象 如:Message msg = new Message() ;

复用系统的Message 如:Message msg = handle.obtainMessage();

Android消息机制中引入了消息池, 在新建消息时,会先查询消息池中是否有消息,有则使用,没有就新建一个。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。

Handler处理消息

Looper是用来管理messageQueue的,通过looper.loop()方法不断轮询消息队列的消息,并交由相应的handler处理。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 if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg); msg.recycleUnchecked(); }}

由代码可以看出,loop()方法主要执行了msg.target.dispatchMessage(msg);dispatchMessage()源码如下:

public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}

分析这段代码可知(此分析摘自参考文献2): 1.首先会判断msg.callback存不存在,msg.callback是Runnable类型,如果msg.callback存在,那么说明该Message是通过执行Handler的postXXX系列方法将Message放入到消息队列中的,这种情况下会执行handleCallback(msg),即Runnable的run方法。 2.如果我们不是通过postXXX系列方法将Message放入到消息队列中的,那么msg.callback就是null。mCallback是Hanlder.Callback类型的,在Handler的构造函数中我们可以传递Hanlder.Callback类型的对象,该对象需要实现handleMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们就会让Callback的handleMessage方法来处理Message。 3.如果我们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么我们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,我们需要自己是重写实现该方法。

Handler注意事项

对于正在执行的Message, 调用RemoveMessage,消息不会终止,Handle的removeMessage函数只能移除队列中的Message。

UI主线程会给自己创建一个looper对象。子线程的looper需要手动的创建。但是在子线程中可以将主线程的looper作为构造方法的参数创建handler,就不需要在子线程中创建自己的looper了。

同一个HandleThread中可以有多个Handler,但是只有一个Looper对象,looper在分发消息时根据msg.target 将消息发往对应的handler。

MessageQueue不是普通的队列。

在activity中的handler中含有未执行的delay消息的时候,调用activity.onFinish()方法之后,onDestory()不会立刻被调用。所以一般情况下,在调用onFinish()方法的时候需要清理一下mhandler里面的消息。

本文参考:

[1]http://blog.csdn.net/itachi85/article/details/8035333 [2]http://www.jb51.net/article/76483.htm [3]http://www.cnblogs.com/zhou-guobao/p/5460139.html [4]http://blog.csdn.net/QQ_22075977/article/details/46791329


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表