黑马程序员技术交流社区
标题: 【济南中心】凯哥兵法之handler机制 [打印本页]
作者: 孟凡凯老师 时间: 2016-4-26 19:57
标题: 【济南中心】凯哥兵法之handler机制
本帖最后由 孟凡凯老师 于 2016-4-26 20:04 编辑
【济南中心】凯哥兵法之handler机制
概述:
handler机制,在Android中提供了一种异步回调机制Handler,谷歌规定耗时的操作不能在主线程执行,必须在子线程中执行,否则可能报这个ANR(application not response 应用无响应)。耗时的操作不能放到主线程中执行,必须在子线程中执行,子线程不能够更新UI,只有主线程能够更新UI,引入Handler来解决。
Handler机制的构成:
Andriod提供了Handler和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交 换(MessageExchange)。
Handler:
构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里或者接收Looper从Message Queue取出)所送来的消息,进行处理。
- public Handler() {
- this(null, false);
- }
- 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;
- }
复制代码
当实例化一个Handler的时候,系统通过Looper.myLooper()获取了当前线程保存的Looper实例,并获取了这个Looper实例中保存的消息队列,这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。
- 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);
- }
复制代码Handler对象通过调用sendMessage(Messagemsg)方法,最终通过调用enqueueMessage(MessageQueuequeue, Message msg, long uptimeMillis)方法
将消息发送给消息队列进行处理。
(1)在调用sendMessageAtTime(Messagemsg, long uptimeMillis)方法的时候
拿到消息队列(在创建Handler对象时获取到的),
(2)当消息队列不为null的时候,再调用处理消息入列的方法:
enqueueMessage(MessageQueue queue,Message msg, long uptimeMillis)
这个方法,做了三件事:
①.为消息打上标签:msg.target =this;:将当前的handler对象这个标签贴到传入的message对象上,为Message指定处理者
②.异步处理消息:msg.setAsynchronous(true);,在asyn为true的时候设置
③.将消息传递给消息队列MessageQueue进行处理:queue.enqueueMessage(msg, uptimeMillis);
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
复制代码- public interface Callback {
- public boolean handleMessage(Message msg);
- }
复制代码- public void handleMessage(Message msg) {
- }
复制代码 (1)依据Callback中的handleMessage(msg)的真假判断是否要处理消息,如果是真则不进行消息分发,则不处理消息,否则进行处理消息
(2)当Callback为null或其handleMessage(msg)的返回值为false的时候,进行分发消息,即调用handleMessage(msg)处理消息
(3)复写其中的handleMessage(Message msg),对消息进行处理(更新UI)。在创建Activity之前系统启动的时候,先加载ActivityThread这个类,在这个类中的main函数,调用了Looper.prepareMainLooper()方法进行初始化Looper对象,在创建Handler中,会将Looper设置给handler,并随带着MessageQueue对象;
虽然是在子线程中编写的代码,但是由于传入的是主线程的looper,所以,Looper从MessageQueue队列中轮询获取消息、再进行更新界面的操作都是在主线程中执行的。
Message Queue(消息队列):
用来存放Handler发来的Message对象 ,将Message以链表的方式串联起来的,通过Looper进行轮询获取,在一个线程中的MessageQueue需要一个Looper进行管理。
- boolean enqueueMessage(Message msg, long when) {
- if (msg.isInUse()) {
- throw new AndroidRuntimeException(msg + " This message is already in use.");
- }
- if (msg.target == null) {
- throw new AndroidRuntimeException("Message must have a target.");
- }
- synchronized (this) {
- if (mQuitting) {
- RuntimeException e = new RuntimeException(
- msg.target + " sending message to a Handler on a dead thread");
- Log.w("MessageQueue", e.getMessage(), e);
- return false;
- }
- msg.when = when;
- Message p = mMessages;
- boolean needWake;
- if (p == null || when == 0 || when < p.when) {
- // 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;
- }
- // We can assume mPtr != 0 because mQuitting is false.
- if (needWake) {
- nativeWake(mPtr);
- }
- }
- return true;
复制代码在消息未被处理且handler对象不为null的时候,进行如下操作(同步代码块中执行)
(1)将传入的处理消息的时间uptimeMillis赋值为当前消息的when属性。
(2)将next()方法中处理好的消息赋值给新的消息引用:Message p = mMessages;在next()方法中:不断的从消息池中取出消息,赋值给mMessage,当没有消息发来的时候,Looper的loop()方法由于是阻塞式的,就一直等消息传进来
(3)当传入的时间为0,且next()方法中取出的消息为null的时候,将传入的消息msg入列,排列在消息队列上,此时为消息是先进先出的否则,进入到死循环中,不断的将消息入列,根据消息的时刻(when)来排列发送过来的消息,此时消息是按时间的先后进行排列在消息队列上的
Looper(消息循环器):
一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列), 一般用到到是prepare和loop两个方法。
- // sThreadLocal.get() will return null unless you've called prepare().
- static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
复制代码- 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));
- }
复制代码 可以看到,系统将一个Looper的实例放入了ThreadLocal,并且判断了sThreadLocal是否为null,这也说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例- /**
- * Return the Looper object associated with the current thread. Returns
- * null if the calling thread is not associated with a Looper.
- */
- public static Looper myLooper() {
- return sThreadLocal.get();
- }
复制代码- /**
- * 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.recycle();
- }
- }
复制代码 (1)通过mylooper()获取到了一个Looper对象,并且做了非空判断。
(2)通过me.mQueue获取该Looper的消息队列
(3)开始进入无限循环,通过调动queue.next();方法不断的从消息队列中取出Message信息,通过调用msg.target.dispatchMessage(msg)进行消息分发,其实是调用了Handler中的dispatchMessage(Message msg)方法:
(4)消息分发完之后,调用msg.recycle()回收消息。
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mThread = Thread.currentThread();
- }
复制代码在Looper的构造方法中自动帮我们创建了一个消息队列。
特别注意:
两种情况初始化Looper对象:
1. 在UI Thread即主线程是系统帮忙创建的Looper,不需要显式的创建Looper对象,直接创建Handler对象即可;因为在主线程ActivityThread的main函数中已经自动调用了创建Looper的方法:Looper.prepareMainLooper();,并在最后调用了Looper.loop()方法进行轮询。
2.如果在子线程中创建Handler对象,需要创建Looper对象,即调用显式的调用Looper.prepare();
Message:
进行消息的封装,同时可以指定消息的操作形式
- public static Message obtain() {
- synchronized (sPoolSync) {
- if (sPool != null) {
- Message m = sPool;
- sPool = m.next;
- m.next = null;
- sPoolSize--;
- return m;
- }
- }
- return new Message();
- }
复制代码当我们调用Message.obtain()方法时获取Message对象时,先判断消息池是否有消息(if (sPool != null)),没有则创建新消息对象,有则从消息池中取出消息,并将取出的消息从池中移除,这样可以避免创建过多的Message对象占用内存。
Message常用的方法:
(1)public int what:变量,用于定义此Message属于何种操作
(2)public Object obj:变量,用于定义此Message传递的信息数据,通过它传递信息
(3)public int arg1:变量,传递一些整型数据时使用
(4)public int arg2:变量,传递一些整型数据时使用
(5)public Handler getTarget():普通方法,取得操作此消息的Handler对象。
作者: 济南王昭珽 时间: 2016-4-29 12:13
很好的文章
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |