android学习13#--Handler消息传递机制
来源:互联网 发布:怎么配置linux ip 编辑:程序博客网 时间:2024/05/21 14:03
本文一点一点的把与handler相关的知识点都引了出来,尽最大努力把这个机制讲清楚。
为什么android要求子线程通过Handler来更新UI
我们先来看看官网[https://developer.android.com/training/multiple-threads/communicate-ui.html#Handler]的这段文字:
Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren’t running on your UI thread, they don’t have access to UI objects. To move data from a background thread to the UI thread, use a Handler that’s running on the UI thread.
总的意思是:app启动总是会创建一个单独主线程UI线程,约定组件操作只能再UI线程中完成,其它线程需要操作组件时,需借助Handler来完成。
Handler功能
- [https://developer.android.com/reference/android/os/Handler.html]上的文字:
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有关的message和runnable对象,一个handler对象只能和一个thread有关,每一个handler的创建一定会绑定一个thread或message queue;从那时起,创建handler的线程将会发送message和runnable到message queue,而主线程会从message queue中取出来处理。 - There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
Handler主要有两个用途:(1) message和runnable会在将来的某个时间点执行;(2) 需要再另一个线程中添加入队动作,不是本线程。 - Handler是线程管理框架的一部分,主要是接收子线程发送的数据,并用此数据配合主线程更新UI,用来跟UI主线程交互用。
- 比如在子线程处理一些耗时的操作,然后子线程用handler发送一个message给主线程UI线程的handler来接收、处理该消息更新UI,这样以避免直接在UI主线程中处理事务导致影响UI主线程的其他处理工作,Android提供了Handler作为主线程和子线程的纽带;
- 也可以将handler对象传给其他进程,以便在其他进程中通过handler给你发送事件;
- 还可以通过handler的延时发送message,可以延时处理一些事务的处理。
创建Handle
- Handler():
Default constructor associates this handler with the Looper for the current thread. - Handler(Handler.Callback callback):
Constructor associates this handler with the Looper for the current thread and takes a callback interface in which you can handle messages. - Handler(Looper looper):
Use the provided Looper instead of the default one. - Handler(Looper looper, Handler.Callback callback):
Use the provided Looper instead of the default one and take a callback interface in which to handle messages.
创建looper
上面的4个构造方法中,会发现每一个构造方法都会有一个looper类。先来说说looper类
API中提到:This class contains the code required to set up and manage an event loop based on MessageQueue
意思是Looper类包含了需要建立和管理一个基于MessageQueue的事件代码
[https://developer.android.com/reference/android/os/Looper.html]上讲:
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Looper类是用于处理线程的消息队列的这么一个类。线程默认情况下不带有消息循环;在线程中调用prepare()方法创造looper对象,然后调用loop()方法启动消息处理,直到loop结束。
先来看看looper类的prepare()方法源码:
/** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) {//这个判断证了一个线程中只有一个Looper实例 throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
上面的prepare()方法保证了每一个线程最多只有一个looper对象,注意prepare()是一个静态函数,再来看看looper构造方法源码:
/** * Return the {@link MessageQueue} object associated with the current * thread. This must be called from a thread running a Looper, or a * NullPointerException will be thrown. */ public static @NonNull MessageQueue myQueue() { return myLooper().mQueue; } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
这里会创建一个MessageQueue对象。因此在初始化Looper对象时,会创建一个与之关联的MessageQueue,这个MessageQueue负责管理消息。
再来看看启动消息管理的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) { // 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); } // 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(); } }
loop()方法是采用一个死循环的方式遍历MessageQueue中的message,调用调用msg.target.dispatchMessage(msg)将消息分发给对应的Handler进行处理。
再回到Handler构造函数
我们发现第一个跟第二个构造函数并没有传递Looper,查看源码,这两个构造函数其实调用的是下面这个函数,源码如下:
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; }
你会发现最终会调用mLooper = Looper.myLooper();来获取当前线程绑定的Looper对象。同时通过这个Looper对象获取对应的MessageQueue对象。
第三个跟第四个构造函数实际调用的是如下函数:
public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
looper对象是外面传递进来的,这个不难理解。
再来看看第二个和第四个构造函数有一个参数Callback callback。查看Callback对象,发现它是Handler的内部接口,需要实现内部的handleMessage()方法,接口源码如下:
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public interface Callback { public boolean handleMessage(Message msg); }
很明显这个接口是用来处理消息的方法,该方法通常需要被重写。重写有两个方法:
1. 采用回调的形式,即通过第二个或第四个初始化Handler对象。
2. 采用重写的方法,即通过第一个或第三个初始化Handler对象。
但是实际中具体是哪一种构造方法用得多,我也不是很清楚,先了解这些原理吧,后续实践的时候留意下。
Handler分发消息的一些方法
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
查看源码最后都是调用了sendMessageDelayed()方法,sendMessageDelayed()调用的是sendMessageAtTime()方法,sendMessageAtTime()最终调用的是enqueueMessage();
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;//this就是当前的Handle对象 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
上面的几个函数实现逻辑:把当前的Handler作为msg的target对象,通过Handler的mQueue对象的enqueueMessage()方法将消息放入MessageQueue的消息队列中。
总结
到了这里,总结一下,前面我依次从Handler入手,引入了MessageQueue,由Handler构造函数引入Looper,再由Looper引入MessageQueue,然后又回到Handler构造函数,哈哈,这条路是不是很清晰。
1. Looper:每个线程只有一个looper,负责新建和管理MessageQueue,负责将message从MessageQueue中取出来分发给对应的Handler。
2. MessageQueue:由looper创建并管理。
3. Handler:把消息发送给Looper管理的MessageQueue,并负责处理Looper分发给他的消息。
再来梳理下线程中使用Handler的步骤:
1. 调用Looper的静态prepare()方法为当前线程创建一个looper对象,创建looper对象是,系统会自动创建一个与之配套的MessageQueue。
2. 当前线程有了looper对象和MessageQueue后,就可以创建Handler了,记得根据需求重写handleMessage()方法
3. 最后调用Looper的静态loop()函数启动Looper。
需要特别说明的是,我们的主线程UI Thread在app系统一启动的时候就会创建,同时也会创建一个looper对象,因此再主线程中,我们可直接创建Handler对象,而不需要按照上面的所说的先调用Looper.prepare()等后再创建Handler对象
引用:
http://blog.csdn.net/amazing7/article/details/51424038
http://www.cnblogs.com/angeldevil/p/3340644.html
http://blog.csdn.net/goodlixueyong/article/details/50831958
http://www.jb51.net/article/37465.htm
http://blog.csdn.net/army_jun/article/details/51729351
http://blog.csdn.net/heng615975867/article/details/9194219
http://blog.jobbole.com/73267/
http://www.cnblogs.com/colder/p/3791299.html
- android学习13#--Handler消息传递机制
- 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消息传递机制
- sync命令的作用
- echarts3 柱状图实现
- UI里面excel数据上传并解析的例子(CSV格式)
- 最后还是json格式的问题
- TCP:传输控制协议
- android学习13#--Handler消息传递机制
- EditText焦点的问题。
- 前端面试小结
- 宏定义 #,##
- PopupWindow的使用
- 在hibernate中使用二级缓存的时候指定CacheConcurrencyStrategy.READ_WRITE修改后接着用for-each 或 iterator访问时报错ConcurrentMod
- HDU4403 dfs
- log4j日志
- SQL Server的优化