Android多线程之Handler
来源:互联网 发布:丁香园insight数据库 编辑:程序博客网 时间:2024/06/14 21:32
Handler相关概念
创建handler的线程,我们约定为handler所在线程。
调用handler的线程,可以是handler所在线程,也可以是其他线程,这里我们只讨论在其他线程里调用handler。
MessageQueue,用来存储handler进行的两个操作:send 的 Message 和 post 的 Runnable。
Looper,用来从MessageQueue中依次取出msg并dispatch给handler。
Handler的用处
A Handler allows you to send and process Message and Runnable objects associated with a thread's 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的实例都关联着一个线程以及由这个线程创建的MessageQueue,这里的线程特指创建Handler实例的线程。
这个Handler的实例可以向MessageQueue发送messages和runnables,且对这些messages和runnables从message queue出来的时候进行处理。
在Android编程中,主要在UI线程创建Handler的实例handler,在其他线程调用handler的引用发送消息或者任务,最后在UI线程进行对消息和任务的处理。
运用场景:在UI线程创建handler,在其他线程进行耗时操作,操作结束后发送消息,UI线程接收消息后进行对UI的更新。
Handler发送消息全过程
① 初始化当前线程的Looper
② 创建Handler的实例handler
③ 使用handler发送消息或任务
④ 在Looper和handler所在的线程进行对消息和任务的处理
接下来我们一边来模拟用法,一边讲讲源码吼~~~
① 照理来说呢,我们应该在UI线程初始化Looper,不过呢,这步Android系统环境帮我们做好啦,所以就不用进行初始化了。
在Android源码中,我们可以打开Looper.java文件,里头有这么一个方法prepareMainLooper,很给力地帮我们初始化好了:
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
那么,在别的线程能省略这步嘛,当然是不能辣,否则就会报异常。为啥嘞,第二步我们是不是要new Handler,既然要new,就得看看Handler的主要构造方法:
public Handler(Callback callback, boolean async) { ...... 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; }
public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }看见没,第一个构造方法不用你传Looper,但是如果你在其他线程不对Looper进行初始化,那mLooper就是null,就会抛出异常。
第二个方法就更不用说了,必须得传一个Looper的实例。
那如何进行Looper的初始化呢?(注意这行代码在其他线程才用添加哦,UI线程不用调用):
Looper.prepare();
我们再来看看Looper初始化的时候干了什么
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }看第一句,初始化了一个消息队列呢。所以说,在初始化Looper的过程中,会初始化一个消息队列MessageQueue。
在其他线程的Looper完成初始化后,就可以调用Looper.loop进入消息循环了,后面的handler发送消息之后,轮循器就会去处消息进行处理辣。
② 开始创建handler啦,创建一个类继承自Handler,并创建一个实例handler。
private static DownloadHandler handler = new DownloadHandler(); private static class DownloadHandler extends Handler { }③ 开启另一个线程并模拟耗时任务,在任务结束时使用handler发送消息 handler.sendMessage(Message msg):
private static DownloadHandler handler = new DownloadHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_empty); new Thread(new DownloadTask()).start(); } private static class DownloadHandler extends Handler { } private static class DownloadTask implements Runnable { @Override public void run() { boolean isOver = false; //long-running operations //假设此处有耗时操作,将isOver置为true if (isOver) { Message msg = Message.obtain(); msg.what = 100; msg.obj = new Object(); handler.sendMessage(msg); } } }handler发送消息可以使用下面的7种方法,最常用的为前3种和最后一种:
handler传送任务可以使用下面的5种方法,最常用的为前两种方法:
这里解释一下参数:
what 自定义的消息的code,以便于对消息进行区分,类似于识别码msg 要发送的消息delayMillis 延时发送的时间,如1000,则为1秒之后发送uptimeMillis 在确定的时间发送这个消息
handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + 1000);
handler.sendMessageDelayed(msg, 1000);
上面两行等效;uptimeMillis是相对于系统开机时间的绝对时间;SystemClock.uptimeMillis()是当前的开机时间。
④ 对消息和任务进行处理,我们需要在创建handler的类中重写父类的 handleMessage()方法:
private static DownloadHandler handler = new DownloadHandler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_empty); new Thread(new DownloadTask()).start(); } private static class DownloadHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 100) { Log.i(TAG, "handleMessage: 下载成功"); } } } private static class DownloadTask implements Runnable { @Override public void run() { boolean isOver = false; //long-running operations //假设此处有耗时操作,将isOver置为true if (isOver) { Message msg = Message.obtain(); msg.what = 100; msg.obj = new Object(); handler.sendMessage(msg); } } }
发送Message和Runnable的区别
我们来看看handler.post(Runnable r)方法,可以看到实际上还是调用了sendMessageDelayed方法:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }我们应该注意到,sendMessageDelayed(Message msg, long delayMillis)的 msg 由getPostMessage(r)提供。
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }进一步看,getPostMessage(r) 方法实际将任务 r 封装成一个Message的对象返回。所以两种本质都是发送Message给MessageQueue。
Message如何产生
message可以由构造函数生成,但不建议直接new Message。
Message内部保存了一个缓存的消息对象池,可从消息对象池获取一个消息。
通过Message.obtain(),也可以用Handler.obtainMessage()。
Message使用完后,系统会调用recycle回收,但如果是自己new出的消息,每次使用完后系统放入缓存池,就会占用很多内存。
如下,通过Message的next,使得sPool成为一个Message的缓存链表。
/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } /** * Return a Message instance to the global pool. * <p> * You MUST NOT touch the Message after calling this function because it has * effectively been freed. It is an error to recycle a message that is currently * enqueued or that is in the process of being delivered to a Handler. * </p> */ public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); }
如何发送消息到MessageQueue
以handler.sendMessage(Message msg)为例,我们继续往下看,发现最终执行到了queue.enqueueMessage(msg, uptimeMillis)方法,即消息队列中的方法。
//Handler.java public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }那我们来看看这个enqueueMessage方法写了什么
//MessageQueue.java boolean enqueueMessage(Message msg, long when) { ...... synchronized (this) { ...... Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) {//p==null表明当前的消息队列是空的 // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } ...... } return true; }我们可以看到,其实MessageQueue就是一个单链表。
每次执行这个方法,
如果:
p == null 当前的消息队列为空,或
when == 0 即将要添加的消息的执行时间是0,或
when < p.when 即将要添加的消息的执行时间比消息队列的队头消息的执行时间还要早,则
把这个消息添加到消息队列的队头;
否则:
找到合适的位置将这个消息添加到消息队列
Looper如何进行消息轮循 及 Handler如何处理消息
我们来看一下Looper类中的loop方法
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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 //取出一个消息 if (msg == null) {//消息为空则return掉 // 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 final 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.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg);//msg.target就是发送消息时跟消息相关联的handler } 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();//msg被处理后进行回收 } }首先获取消息队列queue,然后loop每次从queue中取出一个msg,调用msg.target.dispatchMessage(msg)对msg进行处理,最后对msg进行回收。
所以,真正对消息进行处理的地方,就是Handler中的dispatchMessage(msg)方法喽,我们一起来看看:
/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { } /** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) {//handler.post(Runnable r)走这里 handleCallback(msg); } else { if (mCallback != null) {//handler = new Handler(Callback callback)走这里 if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//handler = new Handler()走这里 } }上面我们讲发送Message和Runnable的区别的时候,讲到发送Runnable实质还是发送一个Message,将Runnable封装成一个Message进行发送,msg的callback就是这个runnable。所以分辨一个message是否是Runnable,只需要判断message.callback是否为空,若message.callback != null即为一个Runnable。
so,继续看这个dispatchMessage(msg)方法:
● 当handler发送了一个Runnable时,msg.callback不为null,会直接走handleCallback(msg)方法;
● 当handler发送一个普通Message时,msg.callback为null,这里分为两种情况:
① 创建handler时传入了Callback参数,即handler = new Handler(Callback callback);
② 创建handler时未传入Callback参数,即handler = new Handler();
对于②,创建handler时未传入Callback参数,mCallback == null,则执行Handler子类里重写的handleMessage(msg)方法:
private static DownloadHandler handler = new DownloadHandler(); private static class DownloadHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 100) { Log.i(TAG, "Handler handleMessage: 下载成功"); } } }
对于①,如果mCallback.handlerMessage(msg)返回为false,则继续执行Handler子类里重写的handleMessage(msg)方法:
private static DownloadHandler handler = new DownloadHandler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } }); private static class DownloadHandler extends Handler { public DownloadHandler(Callback callback) { super(callback); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 100) { Log.i(TAG, "handleMessage: 下载成功"); } } }对于①,如果mCallback.handlerMessage(msg)返回为true,则执行callback里头的handlerMessage(msg)方法,return掉,不需要再执行Handler子类里重写的handleMessage(msg)方法:
private static DownloadHandler handler = new DownloadHandler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == 100) { Log.i(TAG, "handleMessage: 下载成功"); } return true; } }); private static class DownloadHandler extends Handler { public DownloadHandler(Callback callback) { super(callback); } }所以我们来猜测一下下面的代码,执行结果是什么~
private static DownloadHandler handler = new DownloadHandler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == 100) { Log.i(TAG, "Callback handleMessage: 下载成功"); } return true; } }); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_empty); new Thread(new DownloadTask()).start(); } private static class DownloadHandler extends Handler { public DownloadHandler(Callback callback) { super(callback); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 100) { Log.i(TAG, "Handler handleMessage: 下载成功"); } } } private static class DownloadTask implements Runnable { @Override public void run() { boolean isOver = false; //long-running operations //假设此处有耗时操作,将isOver置为true isOver = true; if (isOver) { Message msg = Message.obtain(); msg.what = 100; msg.obj = new Object(); handler.sendMessage(msg); } } }执行结果:
Callback handleMessage: 下载成功
总结
过程:
● 创建一个线程A,初始化线程A中的Looper并开始loop,初始化Looper过程中会初始化一个MessageQueue queue。
● 在线程A中创建Handler对象handler。
● 创建一个线程B,使用handler发送消息msg给线程A中的queue。
● looper 负责dispatch消息给 handler,handler 负责在线程A中对接收到的msg进行处理。
handler可以发送两种类型的消息:Message,Runnable。
一个handler,对应一个创建它的线程,对应一个Looper和一个MessageQueue。
参考链接:
Android消息处理机制(Handler、Looper、MessageQueue与Message)
android线程间通信之handler
handler类的sendMessageAtTime(Message, long)该怎样理解
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android 多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android--多线程之Handler
- Android多线程之handler
- Android--多线程之Handler
- 03中国开源镜像站点汇总
- Hibernate映射—— 一对多关联映射
- GameExit—PC版
- 你是如何自学 Python 的?
- 【工作笔记】微信公众号页面摇一摇+触发音效
- Android多线程之Handler
- matlab矩阵转置
- c++智能指针实现
- HttpClient使用详解
- java笔试面试题--int数组的默认值
- shell学习整理(6)-数组和关联数组
- 《跃迁》思维导图教你如何练就高手之路
- Spark Sort Based Shuffle内存分析
- 网站图片收录与展现的维度