Android:Handler消息机制(一)——什么是Handler消息机制

一、什么是Android的消息机制

Android的消息机制主要是指Handler的运行机制,handler是一套消息传递异步通信机制,Handler是Android的消息机制的上层接口,在开发过程中只需要和Handler交互即可,目的是将一个耗时任务切换到某个指定的线程中去执行。Handler的运行需要底层的MessageQueue和Lopper一起完成,这三者其实是一个整体。额外使用MessageQueue是为了有序性。

二、为什么需要Handler

Android规定访问UI只能在主线程中进行,如果在子线程中访问UI就会导致程序抛出异常,因为ViewRootImpl会对UI操作进行验证,这个功能是由ViewRootImpl的checkThread方法来完成,使用Handler主要是为了解决主线程不能进行耗时操作的同时,在子线程中无法访问UI的矛盾问题。因为在多线程的情况下对UI控件进行并发访问是不稳定的,UI控件不是线程安全的,加上锁后会导致UI逻辑变的更加复杂同时会降低UI的访问效率,所以采用单线程的方式来处理UI操作。

结果是将工作线程中需要更新UI的操作信息通过Handler传递到UI主线程,在主线程中更新UI,实现工作线程对UI的处理,这就是线程切换通过将Handler.sendMessage() 发送消息所在的线程,切换为 Looper.loop()所在的线程进行回调告知handler进行处理,最终实现异步消息的处理。

更新UI的几种方式:

1. handler.post()和handler.sendMessage()

2. View.post(Runnable)和View.postDelayed(Runnable, long):

3. Activity.runOnUiThread()方法

4.AsyncTask

三、消息机制涉及的部分

在这个机制里涉及到了UI主线程、工作子线程、消息Message、消息队列MessageQueue、处理者Handler、循环器Looper、线程本地数据ThreadLocal:

1.UI主线程:当应用程序启动的时候,会同时自动开启一条主线程,用来处理与UI相关的事件。

 

 

2.工作子线程:人为开启的线程,执行耗时操作。

 

 

3.消息Message:线程间通讯的数据单元,存储需要操作的通信信息。被传递的消息,用来存储数据。

尽管Message有public的默认构造方法,但是最好应该通过Message.obtain()来从消息池中获得空消息对象以节省资源。如果message只需要携带简单的int信息,优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存,同时可以用message.what来标识信息,以便用不同方式处理message。消息的发送时也可以先把数据放入bundle内,在把bundle放入message内(message.setData(Bundle))作为一个消息进行发送

4.消息队列MessageQueue:但是内部实现不是真正意义上的队列,实际上是通过一个单链表来维护消息列表,用来存储处理者Handler发送过来的消息Message。主要功能向消息池投递消息和取走消息池的消息。

MessageQueue是消息队列。遵循先进先出原则,Runnable和Message可以被压入MessageQueue,在内部会采用队列的形式以单链表的结构存储一组消息列表,并对外提供插入和删除的操作分别是enqueueMessage和next。enqueueMessage的作用是往消息队列中插入一条消息,next的作用是从队列中读取一条消息并伴随着删除操作,该方法是一个无限循环的方法,如果消息队列中没有消息就会一直阻塞在这里。

5.处理者Handler:UI主线程和工作子线程的通信媒介,线程消息的主要处理者,添加消息到消息队列MessageQueue同时处理循环器Looper分派过来的消息。是消息辅助类,主要功能是向消息池发送各种消息和处理相应消息事件。

Handler创建时会采用当前线程的Looper来构建内部消息循环,如果当前没有Looper就会报错需要为线程创建Looper。消息的发送可以通过post的一系列以及send的一系列方法来实现,post的一系列方法最终是通过send的方法来实现,通过Handler的post方法将一个Runnable投入到Looper内部去处理,或者通过Handler的sendMessage方法发送一个Message,Handler发送消息的过程是向消息队列中插入一条消息,会通过调用MessageQueue.enqueueMessage()将这个消息放入消息队列,最终消息由Looper交由Handler处理,即Handler的dispatchMessage会被调用,这时Handler就进入了处理消息的阶段。

检查Message的callback是否为null,不为null就通过handleCallback来处理消息,Message的callback是一个Runnable对象,实际上就是Handler的post方法所传递的Runnable参数

6.循环器Looper:消息队列MessageQueue和处理者Handler的通信媒介,不断循环执行,调用loop方法后才开始真正工作,循环取出消息队列MessageQueue的消息,将取出的消息Message发送给对应的处理者Handler,每个线程里只能有一个循环器Lopper,一个循环器Lopper可以绑定多个工作子线程的处理者Handler,即多个工作子线程可往一个循环器Looper所持有的消息队列MessageQueue中发送消息,提供了线程间通讯的可能。

Looper是循环器,负责消息的循环,由于消息队列只是一个存储单元,所以由Looper来完成对消息的处理,会以无限循环的形式去查找是否有新消息,如果有就处理没有就会一直循环阻塞在那里。通过Looper.prepare()为当前线程创建一个Looper接着通过Looper.loop()开启消息循环,Looper提供了quit和quiteSafely来退出一个Looper,二者的区别是:quit会直接退出Looper,而quiteSafely只是设定一个退出标记,然后把消息队列中的已有消息处理完成后才安全退出。在子线程中就会一直处于等待状态,如果不退出就会一直等待状态,如果退出Looper以后这个线程就会立刻结束。主线程的Looper是不允许退出的,但是非主线程可以。

Looper内部有一个重要的成员变量,static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(),一旦import了Looper,sThreadLocal就已经存在并构建完毕,ThreadLocal只局限于它所在的线程,导致每个Looper都是独立的。

7.线程本地数据ThreadLocal:是一个线程内部的数据存储类,它的作用是可以在指定线程中存储数据并且只有指定的线程才能获取到存储的数据,一般来说当某些数据是以线程为作用域,且不同线程有不同副本的时候,就可以考虑采用ThreadLocal,比如对于Handler来说,他们需要获取不同线程的Lopper,这个时候就需要通过ThreadLocal可以轻松在不同线程存储Looper,当Handler创建的时候需要通过ThreadLocal获取到当前线程的Looper,因为Looper的作用域就是线程,不同的线程会具有不同的Looper。

四、涉及部分的对应关系

(1)线程(Thread)、循环器(Looper)、处理者(Handler)之间的对应关系如下:

(2)一个Thread对应一个Looper

(3)一个Looper对应一个MessageQueue

(4)一个MessageQueue会有N个Message

(5)一个Message对应一个Handler

(6)一个Habdler对应一个Looper

(7)一个Looper可以有多个Handler

(8)一个Thread可以有多个Handler

 

关于如何使用Handler请看,Android:Handler消息机制(二)——如何使用Handler消息机制

猜你喜欢

转载自blog.csdn.net/ZytheMoon/article/details/105769544