Handler机制(Android消息机制)

自我理解: 链接

handler机制大致流程图.png

1. 首先有一个Looper对象已经存在,并处于轮询的状态;
相对应的代码,Looper.prepare()-->Looper.loop()

2. Handler发送Message,加入到MQ中
各种发送消息的方法-->equeueMessage()-->MessageQueue.equeueMessage()

3. 轮询取出MQ的队头Message,通过Handler进行回调
MessageQueue.next()-->(handler)msg.target.dispatchMessage()-->handleMessage()[自定义TODO]

Message 【继承Parcelable】

封装了任务携带的参数和处理该任务的Handler

/**
 * User-defined message code so that the recipient can identify
 * what this message is about. Each {@link Handler} has its own name-space
 * for message codes, so you do not need to worry about yours conflicting
 * with other handlers.
 */
public int what; //标识位,用来区别消息

/**
 * arg1 and arg2 are lower-cost alternatives to using
 * {@link #setData(Bundle) setData()} if you only need to store a
 * few integer values.
 */
public int arg1; //用来存储一些整数值,替代setData()存储

/**
 * arg1 and arg2 are lower-cost alternatives to using
 * {@link #setData(Bundle) setData()} if you only need to store a
 * few integer values.
 */
public int arg2; //用来存储一些整数值,替代setData()存储

/**
 * An arbitrary object to send to the recipient.  When using
 * {@link Messenger} to send the message across processes this can only
 * be non-null if it contains a Parcelable of a framework class (not one
 * implemented by the application).   For other data transfer use
 * {@link #setData}.
 *
 * 

Note that Parcelable objects here are not supported prior to * the {@link android.os.Build.VERSION_CODES#FROYO} release. */ //发送给收件人的任意对象。当使用{@link Messenger}跨进程发送消息时, //如果它包含框架类的Parcelable(不是应用程序实现的框架类),则它只能是非null。 //对于其他数据传输,请使用{@link #setData} //2.2版本之前不支持实现Parceable的实体类 public Object obj;

  • Message的创建(2种方法)

    1. 直接实例化一个Message,然后设置其参数
      Message msg = new Message;
      msg.what = 0;
      msg.arg0 = 1; 
      ...
      
    2. Message.obtain() --【推荐】
      在许多情况下可以避免分配新的对象;避免重复创建Message
  • Message注意点:

    1. 尽量通过Message.obtain()的方式构建Message对象,防止Message的多次创建;
    2. 仅有int型参数时 最好使用arg1和arg2,减少内存的使用;

Looper:消息通道(使普通线程变成Looper线程)

在Activity中,系统会自动启动Looper对象;而在自定义类中,需要自己手动调用;
下面是Looper线程实现的典型示例(源码中的注释中有写到):

class LooperThread extends Thread {
  public Handler mHandler;
  public void run() {
      Looper.prepare();
      mHandler = new Handler() {
          public void handleMessage(Message msg) {
              // process incoming messages here
          }
      };
      Looper.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()}.
  */
"//将当前线程初始化为looper;确保在调用prepare方法之后,调用loop方法;然后结束时调用quit方法;"
public static void prepare() {
    prepare(true);
}

'//一个Thread只能有一个Looper对象'
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));
}
//初始化时会创建一个MessageQueue对象和线程
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
}

关于Looper.loop()系统源码:

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
//在此线程中运行消息队列,确保在结束loop后调用quit
public static void loop() {
    //获取当前looper线程
    final Looper me = myLooper();
    //判断是否调用了prepare
    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(;;)和while(true)  for (;;)指令少,不占用寄存器,而且没有判断跳转'
    for (;;) {
        // 获取消息队列中的message
        Message msg = queue.next(); // might block 
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ... log日志[省略]...
        try {
            '//交给Message对应的Handler处理消息'
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
         ... [省略]...
        msg.recycleUnchecked();
    }
}

总结:
1)一个Thread只能有一个Looper对象;
2)Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行;
3)Looper使一个线程变成Looper线程。

MessageQueue:消息队列

根据源码中的注释翻译如下:
保存由{@link Looper}分派的消息列表的低级类。消息不会直接添加到MessageQueue,而是通过与Looper关联的{@link Handler}对象添加。
您可以使用{@link Looper#myQueue()Looper.myQueue()}检索当前线程的MessageQueue。

/**
 Low-level class holding the list of messages to be dispatched by a
 {@link Looper}.  Messages are not added directly to a MessageQueue,
 but rather through {@link Handler} objects associated with the Looper.

 

You can retrieve the MessageQueue for the current thread with {@link Looper#myQueue() Looper.myQueue()}. */ public final class MessageQueue { ... //获取下一个message方法 Message next(){ ... } //message添加到消息队列的方法 boolean enqueueMessage(Message msg, long when) { ... } }

Handler:消息操作类【重点】

官方注释(抽象的翻译):
Handler允许您发送和处理与线程{@link MessageQueue}关联的{@link Message}和Runnable对象。
每个Handler实例都与一个线程和该线程的消息队列相关联。
当您创建一个新的Handler时,它被绑定到正在创建它的线程的线程/消息队列 - 从那时起,它将消息和runnables传递给该消息队列并在消息出来时执行它们队列。

Handler有两个主要用途:
(1)将消息和runnable安排在将来的某个点上执行;
(2)将要在不同于自己的线程上执行的动作排入队列。

源码分析:
/**
  *默认的构造方法会 关联一个looper
  */
public Handler(Callback callback, boolean async) {
    ...
    //关联looper,不能为null
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    //直接获取looper的消息队列,
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
handler发送消息:

可传递参数包括Message和Runnable,但即使传递Runnable对象,最终也被处理为Message对象,然后执行sendMessageAtTime()方法

/**
 * 在所有待处理消息之后将消息排入消息队列。
 *  如果消息成功的加入到MQ中,就返回true
 */
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);
}
handler消息的处理:

dispatchMessage(msg);此方法在Looper.loop()中调用msg.target.dispatchMessage();这里的msg.traget就是指向当前的handler;
然后Handler中调用handleMessage(msg)方法,来触发我们需要实现的具体逻辑。

延伸:

1. Handler内部如何获取到当前线程的Looper?

答:是通过 ThreadLocal

Handler内部.png
myLooper方法.png
2. 系统为什么不允许子线程中访问UI?

答:Android中的UI控件不是线程安全的,如果在多线程中并发的访问,UI显示状态不可预计。

3. 为何系统不对UI的访问加上锁机制?

答:上锁会让UI访问的逻辑变得复杂,会降低UI访问的效率,也会阻塞某些线程的执行,体现在界面上会显得卡顿运行不畅。

你可能感兴趣的:(Handler机制(Android消息机制))