【Android笔记】Android的消息机制

前言

本篇文章介绍Android的消息机制,消息机制用于Android中的线程切换以及数据处理。所谓的消息机制,就是Handler的运行机制。Handler是消息机制的上层接口,我们开发中只需要使用Handler即可,除了Handler以外,底层还需要MessageQueueLooper的支持。还有一个类ThreadLocal也会介绍下,下面将介绍整个消息的运行机制。


说明

在这里插入图片描述

1、常见问题


1-1、Android为什么会有消息机制 ?

Android中,特定的线程只能去完成一些特定的操作,比较典型场景就是,我们需要在子线程中进行耗时的I/O操作,可能是读取文件获取进行网络访问,当耗时操作完成以后需要在UI上进行改变,由于Android的限制,不能够在子线程中更新UI,这个时候就需要消息机制把更新UI操作切换到主线程去。


1-2、子线程为什么不能更新UI ?

Android中,UI视图控件是线程不安全的,多个线程并发访问可能会导致UI控件出现异常。

那为什么不可以通过加锁来解决这个问题呢?

加锁缺点:加锁机制会让UI访问逻辑变得负责,其次是会降低UI的访问效率。


1-3、Looper内部无限循环为什么不会造成程序ANR ?

1-3-1、为什么会ANR?

一些耗时操作造成了主线程阻塞一定的时间,比如Activity在5s内不能响应用户事件,或者BroadcastReceiveronReceive方法执行超过10s。


1-3-2、为什么Looper阻塞主线程却没有造成ANR?

主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此Looper的循环并不会对CPU性能有过多的消耗。


2、重要角色


2-1、Handler 消息机制上层接口,用于发送接收处理消息

HandlerpostsendMessage方法发送消息。最终调用的都是enqueueMessage方法,就是向我们的消息队列中去插入此消息。


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

Handler处理消息方法

/**
  * Subclasses must implement this to receive messages.
  */
public void handleMessage(Message msg) {

}
 

2-2、Looper 消息轮询器,查询删除消息纷发给Handler处理


//创建Looper对象
 Looper.prepare();

//轮询器查询消息队列的消息
 Looper.loop();
 

2-3、MessageQueue 消息队列,单链表存储消息

MessageQueue主要包含两个操作:插入和读取消息分别使用如下两个方法。使用单链表方式,对于插入和删除操作更加的方便。


boolean enqueueMessage(Message msg, long when) {

}

 Message next(){
 
 }
 

2-4、ThreadLocal 线程数据存储器

内部使用数组来模拟Map来存储线程的数据,数据存储后,只可以在指定的线程中获取到存储的数据。消息机制中存储的是当前线程的Looper对象。

3、源码解析


下面以主线程中创建Handler为例,来看下消息机制的源码,以及通过源码看整个系统的运行过程。
在这里插入图片描述

先看下Handler执行流程,更方便理解源码的执行流程。
在这里插入图片描述

步骤1、Handler-构造函数


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());
          }
      }
      
//看步骤2 ->  主线程中会自动创建Looper对象,这里获取Looper对象,由Looper循环消息纷发给Handler处理
      mLooper = Looper.myLooper();
      // 没有Looper对象会抛异常
      if (mLooper == null) {
          throw new RuntimeException(
              "Can't create handler inside thread that has not called Looper.prepare()");
      }
      // 消息队列MessageQueue对象 作为Looper的成员变量
      mQueue = mLooper.mQueue;
      mCallback = callback;
      mAsynchronous = async;
  }
    

步骤5、Handler纷发消息,并处理消息(自己实现)

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

/**
   * 系统处理消息为空方法,这里使用时要重写该方法
   */
  public void handleMessage(Message msg) {
  
  }
    

上面2-1介绍,Handler发送消息时,是向MessageQueue队列中插入消息


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
      msg.target = this;
      if (mAsynchronous) {
          msg.setAsynchronous(true);
      }
      // 向消息队列中插入消息并且返回是否插入成功
      return queue.enqueueMessage(msg, uptimeMillis);
  }
  

Looper类:

我们知道在主线程中创建Handler对象,系统默认创建Looper对象,以及执行Looper.prepareLooper.loop方法,就是说主线程中是默认有Looper对象的。


步骤2、Looper 初始化,创建Handler时候调用


// 创建Looper对象,并存到当前线程的ThreadLocal中
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));
   }

// 从ThreadLocal中获取当前线程的Looper对象
public static @Nullable Looper myLooper() {
     return sThreadLocal.get();
 }

// 用于存储Looper的TreadLocal对象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();


步骤3、Looper轮询器不断查询消息队列中消息

public static void loop() {
		// 获取Looper对象
       final Looper me = myLooper();
       // 没有Looper对象  抛异常
       if (me == null) {
           throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
       }
       final MessageQueue queue = me.mQueue;	
       
       ...................省略

       for (;;) {
       	// 轮询消息队列 不断的取消息
           Message msg = queue.next(); // might block
           
           // 当消息队列中没有消息时候,跳出无限循环
           if (msg == null) {
               // No message indicates that the message queue is quitting.
               return;
           }
	    .......................省略
         
           try {
           
           	//步骤5 -> msg.target就是Handler对象,纷发消息
               msg.target.dispatchMessage(msg);
               end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
           } finally {
           
           ........................省略
            
           msg.recycleUnchecked();
       }
   }
    

4、消息机制在Android中应用


Android中用到消息机制的最常用的就是AsnycTask,一个异步任务执行框架。

Android多线程系列(一) AsyncTask基本使用以及源码解析


总结

~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值