Android Handler消息机制研习录

Android Handler消息机制研习录

知识这种东西,隔段时间不用,就会忘记,翻来覆去看了几遍源码,可是每次用的时候还是忍不住再去复习一下实现过程,生怕自己忘记了。现在将它记录下来,方便日后查看。
从事Android的同学都知道,因为Android的UI控件不是线程安全的,所以Android系统不允许在子线程中访问UI,如果在多线程中并发访问可能会导致UI处于不可预期的状态。解决办法就是通过Android的消息机制,在子线程中发送消息到UI线程,然后在UI线程中更新UI。
Android的消息机制主要指Handler的运行机制,其工作流程大致是这样:Handler在线程1中发送消息到线程2(创建Handler的线程)中的MessageQueue,线程2中的Looper取出消息然后交给Handler处理,此时就将消息从线程1切换到了线程2。这句话不能理解没关系,下面我们详细分析这个流程,主要涉及以下几方面内容:

  1. MessageQueue的工作原理;
  2. Looper的工作原理;
  3. Handler的工作机制;

MessageQueue的工作原理

MessageQueue是一个消息队列,虽然是队列,但其底层是靠单链表实现的,简单提供入栈和出栈功能。消息出栈时伴随着消息被处理,如果没有消息,则MessageQueue处于阻塞状态。一个MessageQueue被一个Looper对象关联。由于MessageQueue比较简单,所以在此不做太多介绍。

Looper的工作原理

在介绍Looper工作原理之前,需要先弄明白一个数据存储结构ThreadLocal。如果某些数据是以线程为作用域,并且各个线程内的数据互不干扰时,就可以用ThreadLocal进行存储;ThreadLocal通过提供的set、get方法实现维护不同线程中的同一套数据的副本。有了ThreadLocal,那么就可以保证,一个线程中持有一个Looper对象并且与其他线程无任何关系。
在Android的消息机制中,Looper扮演着消息循环的角色,面对MessageQueue这个队列,Looper对象不断的从队列中取出消息进行处理,如果队列没有消息,则Looper同队列一起处于阻塞状态,直到有消息到达。来看一下遍历的代码:

 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
            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);
            } 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();
        }
    }

在Looper.loop()方法内部,首先通过myLooper()方法取出该线程中的Looper对象,myLooper()是如何实现的呢?

 public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

是不是现在已经很明了了,通过ThreadLocal拿到只属于该线程的Looper对象;回到loop()内部,又拿到了MessageQueue对象,然后就进入了无限循环阶段,在这里会遍历MessageQueue,如果MessageQueue的出栈操作处于阻塞状态,即queue.next()阻塞,那么loop()也在此处阻塞;反之,消息就会被msg.target.dispatchMessage(msg)所处理,这里的msg.target事实上指的就是发送消息的Handler对象;由于loop()方法是运行在创建Handler所在的线程,所以Handler.dispatchMessage(msg)也运行在创建Handler所在线程,如果是主线程,那么就可以进行更新UI的操作了;现在,Looper将消息成功的转给Handler进行处理;至于Handler如何处理消息以及如何发送消息,我们留在下一小节介绍。

Handler的工作机制

Handler的工作主要包含发送消息和接收消息,发送消息通过post以及send系列方法实现,但最终都是调用的send的系列方法,接收消息通过dispatchMessage()方法实现。在介绍发送消息和接收消息之前,先看看Handler的构造方法:

public Handler();
public Handler(Callback callback);
public Handler(Looper looper);
public Handler(Looper looper, Callback callback);
public Handler(boolean async);
public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);

这些构造可以分为两类,一类是提供Looper对象,一类是不提供对象;不提供Looper的构造最终调用的是public Handler(Callback callback, boolean async)方法,先来看看这个方法的实现:

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;
    }

这里最主要的就是Looper对象的判断,其它的就是简单的赋值操作;如果创建Handler之前没有调用Looper.prepare()方法来创建Looper对象,那这里就会抛出一个异常;这里可能有人会问,在UI线程创建Handler时并没有调用Looper.prepare()方法,事实上,在UI线程初始化时已经完成了这个操作。而提供Looper对象的构造方法最终调用的是public Handler(Looper looper, Callback callback, boolean async)方法,这个方法只是简单的赋值操作,所以不再贴代码。
了解了Handler的构造方式,现在再来看看Handler是如何发送消息的,流程如下:
Handler.sendMessage()–>Handler.sendMessageDelayed()–>Handler.sendMessageAtTime()–>Handler.enqueueMessage()—>MessageQueue.enqueueMessage();
当调用post方法发送消息时,会将参数Runnable对象作为Message.callback封装进Message中;可见,发送消息最终目的是消息入栈操作。
现在,再来看Handler是如何通过dispatchMessage(msg)接收消息的:

  public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

首先,判断msg的callback是否存在,msg.callback即通过post系列方法发送消息时的参数Runnable对象,如果存在,就执行该Runnable对象;如果不存在,再判断mCallback是否存在,mCallback是Handler的内部接口,用于处理消息,在Handler的构造方法中可以初始化该接口对象,如果mCallback存在,就执行接口方法处理消息;如果mCallback不存在,就执行handleMessage(msg)方法,该方法是一个空方法,子类必须实现该方法才能处理消息。至此,Handler的工作原理分析完成。

总结

一、Handler:
1、一个线程可以有多个Handler,Handler:Looper => n:1;
2、Handler在子线程中通过post()或者sendMessage()方法发送消息,post内部调用sendMessage方法;这一过程是将消息发送到Handler内部的消息队列中; 该Handler内部的Looper对象属于哪个线程,就会将消息发送到哪个线程中;
3、Handler接收到Looper.loop()方法中的消息后,进行处理,现在已经在Looper所在的进程中了,处理逻辑如下:

  • 判断Message.callback();一个Runnable对象,如果存在就执行这个Runnable对象;不存在执行下一步;
  • 判断Handler.Callback();一个接口,创建handler的时候如果不想实现Handler的子类,则用该接口对象作为Handler的参数,接口方法中处理消息;如果该接口对象不存在,执行下一步;
  • Handler的子类(可以是匿名内部类)重新的handleMessage(message msg)方法处理消息;

4、Handler创建时所在的线程(线程1)就是内部Looper所在的线程,但是发送消失是在子线程(线程2)中执行的,线程2中将消息发送到线程1中关联的Looper的MessageQueue中,那么处理也就是在线程1中了;

二、Looper:
1、一个线程只有一个Looper,一个Looper只能关联一个MessageQueue;
2、Looper的作用是遍历MessageQueue,有消息就处理,没有消息就阻塞(因为MessageQueue的next()方法是一个阻塞方法);
3、Looper内部loop()方法调用Handler.dispatchMessage(Message msg)方法,将轮询到的消息交给Handler进行处理;这个过程是在loop()内部,所以也就是将线程切换到了Looper所在的线程;
4、loop()方法是消息传递及线程切换的核心所在;

三、MessageQueue:
1、MQ是一个消息队列,底层是靠一个链表(存取方便)来实现的;
2、MQ消息出栈时伴随着消息被处理,如果没有消息时处于阻塞状态;

四、ThreadLocal:
1、一个数据结构类型,如果某些数据是以线程为作用域,并且各个线程内的数据互不干扰时,可以用ThreadLocal进行存储;
2、一个Looper对象持有一个ThreadLocal对象;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值