消息处理机制

作者: 最有文化的码农 | 来源:发表于2017-02-23 15:14 被阅读80次

    一、消息机制使用场景

    每个 Android应用程序都运行在一个dalvik虚拟机进程中,进程开始的时候会启动一个主线程(MainThread),主线程负责处理和ui相关的事 件,因此主线程通常又叫UI线程。而由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作。如果在非UI线程直接对UI进行了操 作,则会报错:Called From Wrong Thread Exception : only the original thread that created a view hierarchy can touch its views。 对于运算量较大的操作和IO操作,我们需要新开线程来处理这些繁重的工作,以免阻塞UI线程。Android为我们提供了消息循环的机制,我们可以利用这个机制来实现线程间的通信。那么,我们就可以在非UI线程发送消息到UI线程,最终让UI线程来进行UI的操作。

    二、消息机制架构

    上面是消息机制的框架图。从图中看出,消息机制主要有四个核心类:Looper,Handler,Message和Message Queue。

    1)  Looper是消息循环类,它包括了mQueue成员变量;mQueue是消息队列MessageQueue的实例。Looper还包含了loop()方法,通过调用loop()就能进入到消息循环中。

    2)

    Handler是消息句柄类。Handler提供了sendMessage()来向消息队列发送消息;发送消息的API有很多,它们的原理都是一样的,

    这里仅仅只列举了sendMessage()一个。

    此外,Handler还提供了handleMessage()来处理消息队列的消息;这样,用户通过覆盖

    handleMessage()就能处理相应的消息。

    3)

    Message是消息类。Message包含了next,next是Message的实例;由此可见,Message是一个单链表。Message还包

    括了target成员,target是Handler实例。此外,它还包括了arg1,arg2,what,obj等参数,它们都是用于记录消息的相关内

    容。

    4)  MessageQueue是消息队列类,它包含了mMessages成员;mMessages是消息Message的实例。MessageQueue提供了next()方法来获取消息队列的下一则消息。

    我们启动一个应用程序时,ActivityManagerService会为我们的Activity创建并启动一个主线程(ActivityThread

    对象);在启动主线程时,就会创建主线程对应的消息循环,并通过调用loop()进入到消息循环中。当我们需要往消息队列发送消息时,可以继承

    Handler类,然后创建Handler类的实例;接着,通过该实例的sendMessage()方法就可以向消息队列发送消息。

    也就是说,主线程的消息队列也一直存在的。当消息队列中没有消息时,消息队列会进入空闲等待状态;当有消息时,则消息队列会进入运行状态,进而将相应的消

    息发送给handleMessage()进行处理。

    ActivityThread创建代码如下:

    常在新打开一个应用程序界面时,ActivityManagerService会为该应用启动一个新的新建ActivityThread作为该改应用的主

    线程。该主线程的main函数主要做了两件事:1.新建ActivityThread对象。 2.使用主线程进入消息循环。

    三、关键类详细介绍

    1) Looper-线程的魔法师

    Looper成员变量

    Looper.prepare() && Looper.prepareMainLooper()

    通过源码,prepare()背后的工作方式一目了然,其核心就是将looper对象定义为ThreadLocal。每个线程只能有一个looper。

    Looper.loop()

    调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。

    Looper总结有如下几点:

    每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal

    Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行

    Looper使一个线程变成Looper线程。

    2)Handler-异步处理大师

    a. handler发送消息

    有了handler之后,我们就可以使用post(Runnable),postAtTime(Runnable, long),postDelayed(Runnable, long),sendEmptyMessage(int),sendMessage(Message),sendMessageAtTime(Message, long)sendMessageDelayed(Message, long)这些方法向MQ上发送消息了。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了。源码如下:

    handler发出的消息有如下特点:

    message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler

    post发出的message,其callback为Runnable对象

    handler处理消息

    消息的处理主要通过dispatchMessage和handlerMessage来实现的,其源码如下:

    3)  Message-任务封装

    Message

    是线程之间传递信息的载体,包含了对消息的描述和任意的数据对象。Message中包含了两个额外的

    int字段和一个object字段,这样在大部分情况下,使用者就不需要再做内存分配工作了。虽然Message的构造函数是public的,但是最好是

    使用Message.obtain( )或Handler.obtainMessage(

    )函数来获取Message对象,因为Message的实现中包含了回收再利用的机制,可以提供效率。

    Message结构定义:

    消息获取接口:

    消息回收接口:

    四、ThreadLocal简介

    使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,

    而不会影响其它线程所对应的副本。从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。

    ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:

    void set(Object value)

    设置当前线程的线程局部变量的值。

    public Object get()

    该方法返回当前线程所对应的线程局部变量。

    public void remove()

    将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

    protected Object initialValue()

    返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。

    set方法实现:

    相关文章

      网友评论

        本文标题:消息处理机制

        本文链接:https://www.haomeiwen.com/subject/qrrbwttx.html