Android系列(一) 要了解的Android的消息机制
前言
本篇文章介绍Android的消息机制,消息机制用于Android中的线程切换以及数据处理。所谓的消息机制,就是Handler的运行机制。Handler是消息机制的上层接口,我们开发中只需要使用Handler即可,除了Handler以外,底层还需要MessageQueue和Looper的支持。还有一个类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内不能响应用户事件,或者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为例,来看下消息机制的源码,以及通过源码看整个系统的运行过程。
先看下Handler执行流程,更方便理解源码的执行流程。
Hanlder类:
步骤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.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(安卓)GWES分析
- 关于android中使用new Message的内存泄露问题
- Android消息机制
- android中Invalidate和postInvalidate的区别
- Android线程优先级设置方法
- Android(安卓)Handler
- Handler机制分析
- Android简明开发教程十九:线程 Bezier曲线
- Android异步处理常用方法