A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 孟凡凯老师 于 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取出)所送来的消息,进行处理。
  1.     public Handler() {
  2.         this(null, false);
  3.     }
  4.    public Handler(Callback callback, boolean async) {
  5.         if (FIND_POTENTIAL_LEAKS) {
  6.             final Class<? extends Handler> klass = getClass();
  7.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
  8.                     (klass.getModifiers() & Modifier.STATIC) == 0) {
  9.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
  10.                     klass.getCanonicalName());
  11.             }
  12.         }

  13.         mLooper = Looper.myLooper();
  14.         if (mLooper == null) {
  15.             throw new RuntimeException(
  16.                 "Can't create handler inside thread that has not called Looper.prepare()");
  17.         }
  18.         mQueue = mLooper.mQueue;
  19.         mCallback = callback;
  20.         mAsynchronous = async;
  21.     }
复制代码
当实例化一个Handler的时候,系统通过Looper.myLooper()获取了当前线程保存的Looper实例,并获取了这个Looper实例中保存的消息队列,这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了。
  1.         public final boolean sendMessage(Message msg) {
  2.                 return sendMessageDelayed(msg, 0);
  3.         }
复制代码
  1. public final boolean sendMessageDelayed(Message msg, long delayMillis) {
  2.      if (delayMillis < 0) {
  3.           delayMillis = 0;
  4.      }
  5.      return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
复制代码
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  2.      MessageQueue queue = mQueue;
  3.      if (queue == null) {
  4.           RuntimeException e = new RuntimeException(this+ " sendMessageAtTime() called with no mQueue");
  5.           Log.w("Looper", e.getMessage(), e);
  6.           return false;
  7.      }
  8.      return enqueueMessage(queue, msg, uptimeMillis);
复制代码
  1.     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  2.      msg.target = this;
  3.      if (mAsynchronous) {
  4.           msg.setAsynchronous(true);
  5.      }
  6.      return queue.enqueueMessage(msg, uptimeMillis);
  7.     }
复制代码
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);
  1.     public void dispatchMessage(Message msg) {
  2.           if (msg.callback != null) {
  3.                handleCallback(msg);
  4.           } else {
  5.           if (mCallback != null) {
  6.                if (mCallback.handleMessage(msg)) {
  7.                     return;
  8.                }
  9.           }
  10.           handleMessage(msg);
  11.           }
  12.     }
复制代码
  1.     public interface Callback {
  2.      public boolean handleMessage(Message msg);
  3.     }
复制代码
  1.     public void handleMessage(Message msg) {
  2.     }
复制代码
(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进行管理。
  1. boolean enqueueMessage(Message msg, long when) {
  2.      if (msg.isInUse()) {
  3.           throw new AndroidRuntimeException(msg + " This message is already in use.");
  4.      }
  5.      if (msg.target == null) {
  6.           throw new AndroidRuntimeException("Message must have a target.");
  7.      }

  8.      synchronized (this) {
  9.           if (mQuitting) {
  10.                RuntimeException e = new RuntimeException(
  11.                msg.target + " sending message to a Handler on a dead thread");
  12.                Log.w("MessageQueue", e.getMessage(), e);
  13.                return false;
  14.           }

  15.           msg.when = when;
  16.           Message p = mMessages;
  17.           boolean needWake;
  18.           if (p == null || when == 0 || when < p.when) {
  19.           // New head, wake up the event queue if blocked.
  20.           msg.next = p;
  21.           mMessages = msg;
  22.           needWake = mBlocked;
  23.           } else {
  24.          // Inserted within the middle of the queue.  Usually we don't have to wake
  25.          // up the event queue unless there is a barrier at the head of the queue
  26.          // and the message is the earliest asynchronous message in the queue.
  27.           needWake = mBlocked && p.target == null && msg.isAsynchronous();
  28.           Message prev;
  29.           for (;;) {
  30.                prev = p;
  31.                p = p.next;
  32.                if (p == null || when < p.when) {
  33.                     break;
  34.                }
  35.                if (needWake && p.isAsynchronous()) {
  36.                     needWake = false;
  37.                }
  38.           }
  39.           msg.next = p; // invariant: p == prev.next
  40.           prev.next = msg;
  41.        }

  42.        // We can assume mPtr != 0 because mQuitting is false.
  43.        if (needWake) {
  44.             nativeWake(mPtr);
  45.        }
  46.    }
  47.    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两个方法。
  1.     // sThreadLocal.get() will return null unless you've called prepare().
  2.     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
复制代码
  1.     public static void prepare() {
  2.      prepare(true);
  3.     }

  4.     private static void prepare(boolean quitAllowed) {
  5.      if (sThreadLocal.get() != null) {
  6.           throw new RuntimeException("Only one Looper may be created per thread");
  7.      }
  8.      sThreadLocal.set(new Looper(quitAllowed));
  9.     }
复制代码
可以看到,系统将一个Looper的实例放入了ThreadLocal,并且判断了sThreadLocal是否为null,这也说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例
  1.     /**
  2.      * Return the Looper object associated with the current thread.  Returns
  3.      * null if the calling thread is not associated with a Looper.
  4.      */
  5.     public static Looper myLooper() {
  6.      return sThreadLocal.get();
  7.     }
复制代码
  1.     /**
  2.      * Run the message queue in this thread. Be sure to call
  3.      * {@link #quit()} to end the loop.
  4.      */
  5.     public static void loop() {
  6.      final Looper me = myLooper();
  7.      if (me == null) {
  8.           throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  9.      }
  10.      final MessageQueue queue = me.mQueue;

  11.      // Make sure the identity of this thread is that of the local process,
  12.      // and keep track of what that identity token actually is.
  13.      Binder.clearCallingIdentity();
  14.      final long ident = Binder.clearCallingIdentity();

  15.      for (;;) {
  16.      Message msg = queue.next(); // might block
  17.      if (msg == null) {
  18.           // No message indicates that the message queue is quitting.
  19.           return;
  20.      }

  21.      // This must be in a local variable, in case a UI event sets the logger
  22.      Printer logging = me.mLogging;
  23.      if (logging != null) {
  24.           logging.println(">>>>> Dispatching to " + msg.target + " " +
  25.           msg.callback + ": " + msg.what);
  26.      }

  27.      msg.target.dispatchMessage(msg);

  28.      if (logging != null) {
  29.           logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  30.      }

  31.      // Make sure that during the course of dispatching the
  32.      // identity of the thread wasn't corrupted.
  33.      final long newIdent = Binder.clearCallingIdentity();
  34.      if (ident != newIdent) {
  35.           Log.wtf(TAG, "Thread identity changed from 0x"
  36.           + Long.toHexString(ident) + " to 0x"
  37.           + Long.toHexString(newIdent) + " while dispatching to "
  38.           + msg.target.getClass().getName() + " "
  39.           + msg.callback + " what=" + msg.what);
  40.      }

  41.      msg.recycle();
  42.      }
  43. }
复制代码
   (1)通过mylooper()获取到了一个Looper对象,并且做了非空判断。
   (2)通过me.mQueue获取该Looper的消息队列
   (3)开始进入无限循环,通过调动queue.next();方法不断的从消息队列中取出Message信息,通过调用msg.target.dispatchMessage(msg)进行消息分发,其实是调用了Handler中的dispatchMessage(Message msg)方法:
   (4)消息分发完之后,调用msg.recycle()回收消息。
  1.     private Looper(boolean quitAllowed) {
  2.      mQueue = new MessageQueue(quitAllowed);
  3.      mThread = Thread.currentThread();
  4.     }
复制代码
在Looper的构造方法中自动帮我们创建了一个消息队列。
特别注意:
两种情况初始化Looper对象:
       1. 在UI Thread即主线程是系统帮忙创建的Looper,不需要显式的创建Looper对象,直接创建Handler对象即可;因为在主线程ActivityThread的main函数中已经自动调用了创建Looper的方法:Looper.prepareMainLooper();,并在最后调用了Looper.loop()方法进行轮询。
       2.如果在子线程中创建Handler对象,需要创建Looper对象,即调用显式的调用Looper.prepare();

Message
     进行消息的封装,同时可以指定消息的操作形式
  1.     public static Message obtain() {
  2.      synchronized (sPoolSync) {
  3.           if (sPool != null) {
  4.                 Message m = sPool;
  5.                 sPool = m.next;
  6.                 m.next = null;
  7.                 sPoolSize--;
  8.                 return m;
  9.           }
  10.      }
  11.      return new Message();
  12.     }
复制代码
当我们调用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对象。


1 个回复

正序浏览
很好的文章
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马