1、概述

Handler的消息通信机制是Android提供的一套消息异步传递机制,其主要作用是实现子线程对UI的线程的更新,实现异步消息的处理。在安卓开发中,为了保证UI操作的线程安全,Android规定只有在主线程也就是UI线程才能修改Activity里面的UI组件,同时要求在主线程中尽量不要进行耗时操作,应该尽量把耗时操作放在其他线程中进行。但往往在其他线程中处理完耗时操作得到相应结果后,需要对UI进行操作展示。这时候就出现了矛盾,Handler消息传递机制就是用来解决这个矛盾的。

Handler处理流程

下面是一个简单的应用实例:

public class Activity extends android.app.Activity {    private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            System.out.println(msg.what);        }    };    @Override    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {        super.onCreate(savedInstanceState, persistentState);        setContentView(R.layout.activity_main);        new Thread(new Runnable() {            @Override            public void run() {                ...............耗时操作                Message message = Message.obtain();                message.what = 1;                mHandler.sendMessage(message);            }        }).start();    }}

2、模型及架构

Handler 消息通信机制中主要包含3个部分:MessageQueue,Handler和Looper。以及一个封装消息的类Message。

① MessageQueue:

消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)。

② Handler:

消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage)。

③ Looper:

不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

④ Message

需要传递的消息,可以传递数据。

在子线程执行完耗时操作,当Handler发送消息时,将会调用MessageQueue.enqueueMessage,向消息队列中添加消息。当通过Looper.loop开启循环后,会不断地从线程池中读取消息,即调用MessageQueue.next,然后调用目标Handler(即发送该消息的Handler)的dispatchMessage方法传递消息,然后返回到Handler所在线程,目标Handler收到消息,调用handleMessage方法,接收消息,处理消息。

架构

每个线程中只能存在一个Looper,Looper是保存在ThreadLocal中的。主线程(UI线程)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper。每个线程中可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。 Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler。

类关系

3、源码分析

3.1 Looper

3.1.1 prepare()

要想使用消息机制,首先要创建一个Looper。需要调用prepare():

public static final void prepare() {//判断sThreadLocal是否为null,否则抛出异常//即Looper.prepare()方法不能被调用两次//也就是说,一个线程中只能对应一个Looper实例        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }//sThreadLocal是一个ThreadLocal对象,用于在一个线程中存储变量//实例化Looper对象并存放在ThreadLocal//这说明Looper是存放在Thread线程里的        sThreadLocal.set(new Looper(true));}//再来看下Looper的构造方法private Looper(boolean quitAllowed) {//创建了一个MessageQueue(消息队列)//这说明,当创建一个Looper实例时,会自动创建一个与之配对的MessageQueue(消息队列)        mQueue = new MessageQueue(quitAllowed);        mRun = true;        mThread = Thread.currentThread();}

无参情况下,默认调用prepare(true);表示的是这个Looper可以退出,而对于false的情况则表示当前Looper不可以退出。从上面代码看到,不能重复创建Looper,只能创建一个。创建Looper,并保存在ThreadLocal。ThreadLocal是线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

3.1.2 loop()

开启Looper需要调用loop():

public static void loop() {//myLooper()方法作用是返回sThreadLocal存储的Looper实例,如果me为null,loop()则抛出异常//也就是说loop方法的执行必须在prepare方法之后运行//也就是说,消息循环必须要先在线程当中创建Looper实例        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }//获取looper实例中的mQueue(消息队列)        final MessageQueue queue = me.mQueue;        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();//进入消息循环        for (;;) {//next()方法用于取出消息队列里的消息//如果取出的消息为空,则线程阻塞            Message msg = queue.next(); // might block            if (msg == null) {                return;            }            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }//消息派发:把消息派发给msg的target属性,然后用dispatchMessage方法去处理//Msg的target其实就是handler对象,下面会继续分析            msg.target.dispatchMessage(msg);            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            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();        }}

loop()进入循环模式,不断重复下面的操作,直到消息为空时退出循环,读取MessageQueue的下一条Message,把Message分发给相应的target。当next()取出下一条消息时,队列中已经没有消息时,next()会无限循环,产生阻塞。等待MessageQueue中加入消息,然后重新唤醒。主线程中不需要自己创建Looper,这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。

3.2 Handler

3.2.1 Handler构造方法

创建Handler:

public Handler() {        this(null, false);}public Handler(Callback callback, boolean async) {        if (FIND_POTENTIAL_LEAKS) {            final Class 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());            }        }//通过Looper.myLooper()获取了当前线程保存的Looper实例,如果线程没有Looper实例那么会抛出异常//这说明在一个没有创建Looper的线程中是无法创建一个Handler对象的//所以说我们在子线程中创建一个Handler时首先需要创建Looper,并且开启消息循环才能够使用这个Handler。        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }//获取了这个Looper实例中保存的MessageQueue(消息队列)//这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

对于Handler的无参构造方法,默认采用当前线程TLS中的Looper对象,并且callback回调方法为null,且消息为同步处理方式。只要执行的Looper.prepare()方法,那么便可以获取有效的Looper对象。

3.2.2 Handler 发送消息

发送消息有几种方式,但是归根结底都是调用了sendMessageAtTime()方法。在子线程中通过Handler的post()方式或send()方式发送消息,最终都是调用了sendMessageAtTime()方法。

① post():

public final boolean post(Runnable r)    {      return  sendMessageDelayed(getPostMessage(r), 0);    }public final boolean postAtTime(Runnable r, long uptimeMillis)    {        return sendMessageAtTime(getPostMessage(r), uptimeMillis);    }public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)    {        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);    }public final boolean postDelayed(Runnable r, long delayMillis)    {        return sendMessageDelayed(getPostMessage(r), delayMillis);    }

② send():

public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }public final boolean sendEmptyMessage(int what)    {        return sendEmptyMessageDelayed(what, 0);    }public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {        Message msg = Message.obtain();        msg.what = what;        return sendMessageDelayed(msg, delayMillis);    }public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {        Message msg = Message.obtain();        msg.what = what;        return sendMessageAtTime(msg, uptimeMillis);    }public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }

③ runOnUiThread(Runnable action):

在子线程中调用该方法可以直接更新UI线程,其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。

public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }

如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,最终会调用sendMessageAtTime()方法。否则就直接调用Runnable对象的run()方法。

④ sendMessageAtTime():

所有发送消息的方法最终都调用了该方法,来看一下它的源码。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {//直接获取MessageQueue        MessageQueue queue = mQueue;        if (queue == null) {            RuntimeException e = new RuntimeException(                    this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);            return false;        }//调用了enqueueMessage方法        return enqueueMessage(queue, msg, uptimeMillis);    }//调用sendMessage方法其实最后是调用了enqueueMessage方法private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {//为msg.target赋值为this,也就是把当前的handler作为msg的target属性//如果大家还记得Looper的loop()方法会取出每个msg然后执行msg.target.dispatchMessage(msg)去处理消息,其实就是派发给相应的Handler        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }//最终调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。        return queue.enqueueMessage(msg, uptimeMillis);    }

可以看到sendMessageAtTime()`方法会调用MessageQueue的enqueueMessage()方法,往消息队列中添加一个消息。

3.2.3 Handler 处理消息

分析looper时,其中有一句代码:

msg.target.dispatchMessage(msg);

里面的msg.target就是发送该msg的Handler ,也是同一个Handler 来处理这个消息。

public void dispatchMessage(Message msg) {//一开始就会进行判断//如果msg.callback属性不为null,则执行callback回调,也就是我们的Runnable对象        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

当Message的msg.callback不为空时,则回调方法msg.callback.run();当Handler的mCallback不为空时,则回调方法mCallback.handleMessage(msg);最后调用Handler自身的回调方法handleMessage(),该方法默认为空,Handler子类通过覆写该方法来完成具体的逻辑。从源码中我们可以看到,Message的回调方法:message.callback.run(),优先级最高;Handler中Callback的回调方法:Handler.mCallback.handleMessage(msg),优先级次之;Handler的默认方法:Handler.handleMessage(msg),优先级最低。对于很多情况下,消息分发后的处理方法是第3种情况,即Handler.handleMessage(),一般地往往通过覆写该方法从而实现自己的业务逻辑。

3.3 MessageQueue

3.3.1 将消息插入队列

在Handler发送消息的源码sendMessageAtTime()中有一行消息入队的代码:return enqueueMessage(queue, msg, uptimeMillis);其最后是调用了MessageQueue的enqueueMessage(Message msg,longwhen)。

来看一下其内部源码:

boolean enqueueMessage(Message msg, long when) {    // 每一个Message必须有一个target    if (msg.target == null) {        throw new IllegalArgumentException("Message must have a target.");    }    if (msg.isInUse()) {        throw new IllegalStateException(msg + " This message is already in use.");    }    synchronized (this) {        if (mQuitting) {  //正在退出时,回收msg,加入到消息池            msg.recycle();            return false;        }        msg.markInUse();        msg.when = when;        Message p = mMessages;        boolean needWake;        if (p == null || when == 0 || when < p.when) {            //p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支            msg.next = p;            mMessages = msg;            needWake = mBlocked;        } else {            //将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非            //消息队头存在barrier,并且同时Message是队列中最早的异步消息。            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;            prev.next = msg;        }        if (needWake) {            nativeWake(mPtr);        }    }    return true;}

MessageQueue是按照Message触发时间的先后顺序排列,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。

3.3.2 将消息从队列中取出

当发送了消息后,在MessageQueue维护了消息队列,然后在Looper中通过loop()方法,不断地获取消息。上面对loop()方法进行了介绍,其中最重要的是调用了queue.next()方法,通过该方法来提取下一条信息。看一下起next()源码。

Message next() {    final long ptr = mPtr;    if (ptr == 0) { //当消息循环已经退出,则直接返回        return null;    }    int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1    int nextPollTimeoutMillis = 0;    for (;;) {        if (nextPollTimeoutMillis != 0) {            Binder.flushPendingCommands();        }        //阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回        nativePollOnce(ptr, nextPollTimeoutMillis);        synchronized (this) {            final long now = SystemClock.uptimeMillis();            Message prevMsg = null;            Message msg = mMessages;            if (msg != null && msg.target == null) {                //当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,为空则退出循环。                do {                    prevMsg = msg;                    msg = msg.next;                } while (msg != null && !msg.isAsynchronous());            }            if (msg != null) {                if (now < msg.when) {                    //当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                } else {                    // 获取一条消息,并返回                    mBlocked = false;                    if (prevMsg != null) {                        prevMsg.next = msg.next;                    } else {                        mMessages = msg.next;                    }                    msg.next = null;                    //设置消息的使用状态,即flags |= FLAG_IN_USE                    msg.markInUse();                    return msg;  //成功地获取MessageQueue中的下一条即将要执行的消息                }            } else {                //没有消息                nextPollTimeoutMillis = -1;            }        //消息正在退出,返回null            if (mQuitting) {                dispose();                return null;            }            ...............................    }}

nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。可以看出next()方法根据消息的触发时间,获取下一条需要执行的消息,队列中消息为空时,则会进行阻塞操作来阻塞线程,直到有新消息到来。

4、总结

整个Handler 消息通信机制可用如下图来理解:

Handler机制

更多相关文章

  1. Android(安卓)UI开发第二十四篇――Action Bar
  2. Android(安卓)adt v22.6.2-1085508 自己主动创建 appcompat_v7
  3. Android中线程池的使用分析
  4. CSDN日报20170828——《4个方法快速打造你的阅读清单》
  5. android通过两种方法开启一个线程
  6. Android中使用javah生成jni头文件的正确方法
  7. Android高级工程师BAT面试题及知识点整理大全(Java基础+Android模
  8. 基于Bmob的Android即时通讯应用源码解析
  9. Android(安卓)主流开源框架(七)Glide 的缓存机制

随机推荐

  1. Android异步任务AsyncTask实现方式
  2. Android——Tomcat+MySQL+Servlet,实现将C
  3. Android异步消息处理机制 全解析
  4. 我的第一个Android软件——简单拨号器
  5. 开源项目之Android框架(Query)
  6. EditText控件设置只读
  7. Android仿微信聊天界面
  8. 在Android使用正则表达式
  9. Android开源项目第二篇——工具库篇
  10. 更改android开机画面 DIY