Android系列(一) 要了解的Android的消息机制

前言

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

目录

Android系列(一) 要了解的Android的消息机制_第1张图片

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内不能响应用户事件,或者BroadcastReceiver的onReceive方法执行超过10s。

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

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


2、重要角色

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

Handler通post()和sendMessage()方法发送消息。最终调用的都是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为例,来看下消息机制的源码,以及通过源码看整个系统的运行过程。
Android系列(一) 要了解的Android的消息机制_第2张图片

先看下Handler执行流程,更方便理解源码的执行流程。
Android系列(一) 要了解的Android的消息机制_第3张图片

Hanlder类:

步骤1、Handler-构造函数:

  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());
            }
        }
        
		//看步骤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.prepare(),和Looper.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 sThreadLocal = new ThreadLocal();

步骤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基本使用以及源码解析

你可能感兴趣的:(Android)