Android 消息机制详解及实例代码

作者:lqh 时间:2023-07-28 08:37:49 

Android 消息机制

1.概述

Android应用启动时,会默认有一个主线程(UI线程),在这个线程中会关联一个消息队列(MessageQueue),所有的操作都会被封装成消息队列然后交给主线程处理。为了保证主线程不会退出,会将消息队列的操作放在一个死循环中,程序就相当于一直执行死循环,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数(handlerMessage),执行完成一个消息后则继续循环,若消息队列为空,线程则会阻塞等待。因此不会退出。如下图所示:

Android 消息机制详解及实例代码

Handler 、 Looper 、Message有啥关系?

在子线程中完成耗时操作,很多情况下需要更新UI,最常用的就是通过Handler将一个消息Post到UI线程中,然后再在Handler的handlerMessage方法中进行处理。而每个Handler都会关联一个消息队列(MessageQueue),Looper负责的就是创建一个MessageQueue,而每个Looper又会关联一个线程(Looper通过ThreadLocal封装)。默认情况下,MessageQueue只有一个,即主线程的消息队列。

上面就是Android消息机制的基本原理,如果想了解更详细,我们从源码开始看。

2.源码解读

(1)ActivityThread主线程中启动启动消息循环Looper


public final class ActivityThread {
 public static void main(String[] args) {
   //代码省略
   //1.创建消息循环的Looper
   Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
   thread.attach(false);
   if (sMainThreadHandler == null) {
     sMainThreadHandler = thread.getHandler();
   }
   AsyncTask.init();

//2.执行消息循环
   Looper.loop();
   throw new RuntimeException("Main thread loop unexpectedly exited");
 }
}

ActivityThread通过Looper.prepareMainLooper()创建主线程的消息队列,最后执行Looper.loop()来启动消息队列。Handler关联消息队列和线程。

(2)Handler关联消息队列和线程


public Handler(Callback callback, boolean async) {
   //代码省略
   //获取Looper
   mLooper = Looper.myLooper();
   if (mLooper == null) {
     throw new RuntimeException(
       "Can't create handler inside thread that has not called Looper.prepare()");
   }
   //获取消息队列
   mQueue = mLooper.mQueue;
 }

Handler会在内部通过Looper.getLooper()方法来获取Looper对象,并且与之关联,并获取消息队列。那么Looper.getLooper()如何工作的呢?


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

public static @NonNull MessageQueue myQueue() {
   return myLooper().mQueue;
 }
 public static void prepare() {
   prepare(true);
 }
 //为当前线程设置一个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));
 }
 //设置UI线程的Looper
 public static void prepareMainLooper() {
   prepare(false);
   synchronized (Looper.class) {
     if (sMainLooper != null) {
       throw new IllegalStateException("The main Looper has already been prepared.");
     }
     sMainLooper = myLooper();
   }
 }

在Looper类中,myLooper()方法,通过sThreadLocal.get()来获取的,在prepareMainLooper()中调用prepare()方法,在这个方法中创建了一个Looper对象,并将对象设置了sThreadLocal()。这样队列就和线程关联起来了。通过sThreadLocal.get()方法,保证不同的线程不能访问对方的消息队列。

为什么要更新UI的Handler必须在主线程中创建?

因为Handler要与主线程的消息队列关联上,这样handlerMessage才会执行在UI线程,此时UI线程才是安全的。

(3)消息循环,消息处理

消息循环的建立就是通过Looper.loop()方法。源代码如下:


/**
  * Run the message queue in this thread. Be sure to call
  * {@link #quit()} to end the loop.
  */
 public static void loop() {
   final Looper me = myLooper();
   if (me == null) {
     throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
   }
   //1.获取消息队列
   final MessageQueue queue = me.mQueue;
   //2.死循环,即消息循环
   for (;;) {
     //3.获取消息,可能阻塞
     Message msg = queue.next(); // might block
     if (msg == null) {
       // No message indicates that the message queue is quitting.
       return;
     }
     //4.处理消息
     msg.target.dispatchMessage(msg);
     //回收消息
     msg.recycleUnchecked();
   }
 }

从上述程序我们可以看出,loop()方法的实质上是建立一个死循环,然后通过从消息队列中逐个取出消息,最后处理消息。对于Looper:通过Looper.prepare()来创建Looper对象(消息队列封装在Looper对象中),并且保存在sThreadLocal中,然后通过通过Looper.loop()进行消息循环,这两步通常成对出现。


public final class Message implements Parcelable {
 //target处理
 Handler target;
 //Runnable类型的callback
 Runnable callback;
 //下一条消息,消息队列是链式存储的
 Message next;
}

从源码中可以看出,target是Handler类型。实际上就是转了一圈,通过Handler发送消息给消息队列,消息队列又将消息分发给Handler处理。在Handle类中:


//消息处理函数,子类覆写
public void handleMessage(Message msg) {
}

private static void handleCallback(Message message) {
   message.callback.run();
 }

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

从上述程序可以看出,dispatchMessage只是一个分发的方法,如果Run nable类型的callback为空,则执行handleMessage来处理消息,该方法为空,我们会将更新UI的代码写在该函数中;如果callback不为空,则执行handleCallback来处理,该方法会调用callback的run方法。其实这是Handler分发的两种类型,比如post(Runnable callback)则callback就不为空,当我们使用Handler来sendMessage时通常不设置callback,因此,执行handlerMessage。


public final boolean post(Runnable r)
 {
   return sendMessageDelayed(getPostMessage(r), 0);
 }

public String getMessageName(Message message) {
   if (message.callback != null) {
     return message.callback.getClass().getName();
   }
   return "0x" + Integer.toHexString(message.what);
 }

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

从上述程序可以看到,在post(Runnable r)时,会将Runnable包装成Message对象,并且将Runnable对象设置给Message对象的callback,最后会将该对象插入消息队列。sendMessage也是类似实现:


public final boolean sendMessage(Message msg)
 {
   return sendMessageDelayed(msg, 0);
 }

不管是post一个Runnable还是Message,都会调用sendMessageDelayed(msg, time)方法。Handler最终将消息追加到MessageQueue中,而Looper不断地从MessageQueue中读取消息,并且调用Handler的dispatchMessage分发消息,这样消息就源源不断地被产生、添加到MessageQueue、被Handler处理,Android应用就运转起来了。

3.检验


new Thread(){
 Handler handler = null;
 public void run () {
   handler = new Handler();
 };
}.start();

上述代码有问题吗?

Looper对象是ThreadLocal的,即每个线程都用自己的Looper,这个Looper可以为空。但是,当在子线程中创建Handler对象时,如果Looper为空,那么会出现异常。


public Handler(Callback callback, boolean async) {
   //代码省略
   //获取Looper
   mLooper = Looper.myLooper();
   if (mLooper == null) {
     throw new RuntimeException(
       "Can't create handler inside thread that has not called Looper.prepare()");
   }
   //获取消息队列
   mQueue = mLooper.mQueue;
 }

当mLooper为空时,抛出异常。这是因为Looper对象没有创建,因此,sThreadLocal.get()会返回null。Handler的基本原理就是要与MessageQueue建立关联,并且将消息投递给MessageQueue,如果没有MessageQueue,则Handler没有存在的必要,而MessageQueue又被封住在Looper中,因此创建Handler时,Looper一定不能为空。解决办法如下:


new Thread(){
 Handler handler = null;
 public void run () {
   //为当前线程创建Looper,并且绑定到ThreadLocal中
   Looper.prepare()
   handler = new Handler();
   //启动消息循环
   Looper.loop();
 };
}.start();

如果只创建Looper不启动消息循环,虽然不抛出异常,但是通过handler来post或者sendMessage()也不会有效。因为虽然消息会被追加到消息队列,但是并没有启动消息循环,也就不会从消息队列中获取消息并且执行了。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

来源:http://blog.csdn.net/ztchun/article/details/54142787

标签:Android,消息机制
0
投稿

猜你喜欢

  • ArrayList详解和使用示例_动力节点Java学院整理

    2023-08-05 13:15:41
  • Java中去除字符串中所有空格的几种方法

    2023-11-24 04:59:24
  • 解决Mybatis映射文件mapper.xml中的注释问题

    2023-09-17 15:06:30
  • Java如何有效避免SQL注入漏洞的方法总结

    2021-11-22 07:01:54
  • 解决WPF附加属性的Set函数不调用的问题

    2022-09-27 09:52:38
  • Android实现沉浸式通知栏通知栏背景颜色跟随app导航栏背景颜色而改变

    2022-07-31 22:25:47
  • 日常收集C#接口知识(知识全面)

    2022-09-01 19:02:44
  • 解析C#设计模式编程中外观模式Facade Pattern的应用

    2021-08-08 05:10:37
  • Java基于Socket实现网络编程实例详解

    2023-11-23 12:22:37
  • Java多线程高并发中的Fork/Join框架机制详解

    2021-06-17 01:08:08
  • 生产消费者模式实现方式和线程安全问题代码示例

    2023-11-26 19:44:17
  • 解决IDEA和CMD中java命令提示错误: 找不到或无法加载主类的问题

    2023-09-19 02:31:16
  • Android Mms之:对话与联系人关联的总结详解

    2023-12-06 13:12:57
  • 基于Java向zip压缩包追加文件

    2023-10-11 17:18:52
  • Android自定义View实现天气预报折线图

    2023-08-22 14:37:50
  • Java实现线程同步方法及原理详解

    2021-07-29 21:28:13
  • Flutter Recovering Stream Errors小技巧

    2023-09-02 05:02:17
  • Java 多线程Synchronized和Lock的区别

    2023-08-14 11:39:09
  • mybatis的插件机制示例详解

    2023-02-24 23:46:17
  • 详解如何实现SpringBoot的底层注解

    2023-11-23 06:21:04
  • asp之家 软件编程 m.aspxhome.com