1、概述

在安卓程序启动时,会默认在主线程中 运行程序,那如果执行一些耗时的操作则UI就会处于阻塞状态,出现界面卡顿的现象,再者用户的多种操作,系统是如何做到一一处理的,系统又是如何管理这些任务的,答案这就是今天的主题Android的消息机制;

  •  Android处理消息的方式——handler、Looper 和 MessageQueue
  1. Handler :负责将任务添加到队列,执行结束后在主线程执行UI操作
  2. Looper :未绑定的线程开启循环消息队列,并获取消息
  3. MessageQueue :任务队列,保存发送的消息
  • 在进行源码分析之前先提出两个常见的疑问
  1. 为什么子线程直接创建Handler会抛出异常?

  2. Handler究竟执行在哪个线程中?

2、源码分析

对于Handler的使用方法此处不做介绍,在使用是只要注意避免内存泄漏需创建静态Handler,此处从执行过程和原理的角度简单分析Handler的源码,首先大家都知道在主线程中可以直接使用Handler,而在子线程中不可以,有没有想过为什么呢?现在就先看看主线程究竟有什么不同吧,从头看安卓的入口方法ActivityThread.main()

​public static void main(String[] args) {        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");      ...        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }​
  • 上述代码看出在程序启动后,执行三个步骤:
  1. 调用Looper.prepareMainLooper();创建消息循环Looper
  2. 使用 thread.getHandler(); 获取UI线程的Handler
  3. 使用 Looper.loop()开启消息循环

Handler与消息队列关联,而消息队列被封装在Looper中,每个Looper关联一个线程,所以Handler的消息大致为handler作为一个消息处理器,将消息传递到消息队列中,线程中的Looper逐个取出消息并执行,此处的线程即为UI线程,

  • Handler最基本的用法是创建Handler 发送Message到队列,现在看看创建Handler时都做了哪些事
​ public Handler(Callback callback, boolean async) {                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;    }​
  1. 调用Looper.myLooper():获取Looper对象,如果为空则抛出异常,然后调用lopper.mQuene开启消息队列
  • 查看myLooper()方法 
public static @Nullable Looper myLooper() {        return sThreadLocal.get();    }
  1. 在myLooper中看出,Looper从sThreadLocal中获取,那什么时候设置进去的呢?另外从这也可以看出来,只要sThreadLocal没有Looper的实例就会抛出异常,sThreadLocal就是执行的线程,换句话说只要Handler所在的线程中没有Looper的实例就会异常;
  • 接下来看看UI线程开始时创建的Looper对象的方法prepareMainLooper()
​public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }
  • prepareMainLooper() 调用了prepare()方法:
​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));    }
  1. 看到了 sThreadLocal.set(new Looper(quitAllowed)),此处创建Looper对象设置进sThreadLocal中,是不是明白了上面的sThreadLocal。get()获取的Looper是哪里的了。

到这里我们介绍了Handler 和 Looper的创建,以及各自对线程的关联,下面查看如何启动消息循环获取任务并执行,在入口方法的最后调用了Loop.loop()方法,下面查看Looper.loop()方法:

public static void loop() {        final Looper me = myLooper();        final MessageQueue queue = me.mQueue;for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                return;            }... msg.target.dispatchMessage(msg);msg.recycleUnchecked();}
  1. 调用myLooper()获取Looper的对象,获取消息队列后调用next()依次取出其中的Message,注意这里是个死循环
  2. 调用Messagemsg.target.dispatchMessage(msg)执行方法
  • 查看Message的源码便知target实际是Handler的对象,说明绕了一圈最后还是Handler的dispatchMessage(msg)处理逻辑
Handler target;Runnable callback;Message next;
  • 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);        }    }private static void handleCallback(Message message) {        message.callback.run();    }

总结一下:looper从MessageQuene中依次取出Message,然后调用Msg绑定的Handler的dispatchMessage(msg)处理,dispatchMessage()中依次判断执行:

  1. 先判断Message的Runable对象callback是否为null,若不为空使用handleCallback()即调用Runnable,run()执行
  2. 若callback为空判断mCallBack,若mCallBack不为空则调用mCallBack.handleMessage()处理
  3. 如果上面两者都为null,则执行handleMessage(),此处即为创建Handler()重写的handlerMessage()

到此Android中Handler的消息处理,从创建Handler、Looper和MessageQueue到消息的传递和事件的处理都介绍完了,下面我们看看使用时如何发送消息到MessageQueue以及上述的callback、mCallBack什么情况下为空?

  • 创建的Handler后发送消息方法:sendMessage(Message msg)、post(Runnable r)
  1. sendMessage()
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;                return enqueueMessage(queue, msg, uptimeMillis);    }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;   // 此处设置target为Handler对象        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

上面的执行过程代码从上倒下一目了然,在enqueueMessage()中设置了Message.target即为调用方法的Handler,所以在调用dispatchMessage()时,会调用Handler的handleMessage()处理

  • post(Runnable r)
public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;   // 设置Message的callback为r        return m;    }
  1. 调用getPostMessage()设置Message中的callback,所以callback就是发送的runnable,其余方法和sendMessage()一致。

Handler消息机制的原理和过程介绍完毕了,带着上面的了解现在来解决上面的两个疑问:

  • 为什么不能在子线程直接创建创建handler吗?

通过上面的源码分析,我们知道抛出的异常在上述代码中也出现了

mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }

当Looper.myLooper()取出为空时抛出异常,而myLooper()中时从sThreadLocal.get()中获取,即此时sThreadLocal中没有设置Looper对象,所以也就没有后面的队列等操作,从上面知道创建并设置Looper对象是在prepare()方法中,所以要在子线程中使用Handler要先调用Looper.prepare()创建Looper对象,后调用Looper.loop()开启消息循环。

  • Handler究竟执行在哪个线程中?

其实针对这个问题,网上很多答案是:执行在Handler()所在的线程,即在哪个线程创建Handler实例,handler后续的任务就执行在哪个线程,其实这个答案可对可错,从我们平时使用来看无论是在子线程初始化Loop后创建Handler(),还是在主线程直接创建,最终Handler所执行的线程就是创建线程,但这只是其中一种情况,下面看看Handler的另一种创建方式:从上面我们知道对于任务的处理在looper.loop()中,以及后面对任务的处理都在同一线程中,整个过程知道HandlerMessage()都没有执行线程切换处理,也就是说调用loop()方法的Looper实例在哪个线程创建就会在哪个线程执行,同时消息队列mQueue也在相同线程,之所以会有人说执行的线程会是Handler创建的线程,其实因为在主线程或子线程使用无参构造函数创建Handler()时,Looper实例和Handler实例在同一线程,所以任务和Handler执行在此线程,到从可以得出结论:Handler真正执行的线程是消息队列Looper()实例的所在线程,关于此结论的验证,代码很简单请读者自行验证;

 

更多相关文章

  1. Android(安卓)和H5之间的交互-框架篇
  2. Android基于IIS的APK下载(四)数据下载
  3. Android的Handler机制
  4. Android参考书籍
  5. Android消息机制源码解析(二)——消息的执行者Handler
  6. Android笔面试
  7. [Android] 面试题总结-猎趣-Android部分
  8. 十分钟了解Android触摸事件原理(InputManagerService)
  9. 第十五章 消息循环模型 - 草稿

随机推荐

  1. Android 'showAsAction' in package 'and
  2. android图片特效处理之光晕效果
  3. android 字体&颜色
  4. android junit入门(一) JUNIT测试
  5. 四大布局
  6. Android Studio(六):Android Studio添加注
  7. ADT对应的.android中找不到adb_usb.ini
  8. android:installLocation 用法科普
  9. Android(安卓)APP首次登录和之后自动登录
  10. Android studio启动后无法更新