Handler的运行机制

来源:互联网 发布:哪个软件有天九牌游戏 编辑:程序博客网 时间:2024/06/06 11:47

概述

Handler机制又称为Android中的消息机制,这是Android中的重点,是最常用的线程间通信的方式。本blog首先介绍android中为什么要提供消息机制,然后以分析原码的形式讲解消息机制中重要的类及类中重要的方法,再讲解各个类之间的调用关系,最后对Handler的执行机制进行总结。

Android中的消息机制

一,Android中为什么要提供消息机制

我们都知道在android中有两个规定: 1. 网络请求操作要放到子线程。 2. 不能在子线程更新UI。所以Android中必须有线程间的通讯机制。那么为什么要有这两个规定呢?

耗时操作为什么要放在子线程中?
答:如果在主线程一直执行代码,那么界面就会卡顿,为了提高用户体验,android增加了ANR机制,即界面长时间没有响应就会出现ANR。我们在开发中既为了避免界面卡顿,又避免出现ANR所以把耗时的操作放在子线程中。

为什么不能在子线程中更新UI?
控件是线程不安全的,如果不同线程同时修改UI控件将产生异常。

为什么不给控件加锁呢?
加锁确实是一个解决的办法,但是加锁会让逻辑变得复杂,降低UI绘制的效率。总体来说不如添加Handler机制处理效率高。

二,Android消息机制概述

Android的消息机制主要是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程,这三者是一个整体。

MessageQueue的中文翻译是消息队列,作用是存储消息,里面是以单链表的结构进行存储。

Looper的中文翻译是循环,这里叫消息循环,MessageQueue的作用是存储消息,looper的作用就是从messageQueue里面取出新消息,然后交由handler进行处理。

此外还有两个重要的类,Message和ThreadLocal。Message用来携带数据。ThreadLocal保证线程内数据安全。

下面开始看各个类的原码,这里仅贴出重要方法的重要方法体,想更详细了解原码的小伙伴可以自行查看原码。

ThreadLocal类

这是一个存储数据的类,提供了set和get方法,一个ThreadLocal对象只能存储一个对象。这个类的最大特点是可以指定线程存取数据,即:在一个线程中使用set方法存储一个数据,只有在该线程中使用get方法才能取出来,在其他线程中使用get方法取不出的,这就保证了线程间数据的安全。

一,set方法

set方法的核心原码如下:

    public void set(T value) {        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values == null) {            values = initializeValues(currentThread);        }        values.put(this, value);    }

分析:在调用ThreadLocal的set方法存储数据时,首先根据当前线程得到一个Values对象,如果得不到就去创建一个Values对象,Values中储存的是键值对,键时当前的ThreadLocal对象,值就是我们存储在ThreadLocal对象中的值。

二,get方法

get方法的核心原码如下:

    public T get() {        // Optimized for the fast path.        Thread currentThread = Thread.currentThread();        Values values = values(currentThread);        if (values != null) {            Object[] table = values.table;            int index = hash & values.mask;            if (this.reference == table[index]) {                return (T) table[index + 1];            }        } else {            values = initializeValues(currentThread);        }        return (T) values.getAfterMiss(this);    }

分析:在调用ThreadLocal的get方法取出对象时,首先也是得到Values对象,如果是同一个线程,则此时一定可以通过values(currentThread)获取到values对象,此时就可以根据某些算法取出通过set方法存进去的对象。

三,ThreadLocal类在Handler机制中的作用

这里先简单说明一下,如果不懂可以跳过去,等看完整篇之后回头再看。

在Handler机制中,需要保证UI线程中只有一个MessageQueue对象和一个Looper对象,系统在app启动时给我们创建了一个Looper对象,并保存到ThreadLocal中,在创建Handler对象时需要得到Looper对象(这个可以看Handler的构造方法),此时我们要保证得到的是UI线程中的Looper对象,就需要使用ThreadLocal。

Message类

Message作用是封装数据,在Handler的使用中已经用到,下面详细介绍一下它几个重要的字段和方法。

一,Message类中重要的字段

Message类中重要的字段如下:
Arg1和arg2可以直接传递int数据。

callBack:是一个回调方法,这个方法在UI线程被调用。

data:是bundle类型,里面可以封装很多数据。

next:是为了消息池而定义的,执行消息池中的下一个Message对象。

obj:传递数据时可以给Message绑定一个对象,比如String,Bitmap,Student,等等。

sPool:维护的消息池。

target:这个是很重要的字段,一个app只有一个主线程,一个线程中只有一个MessageQueue对象和一个Looper对象,不同Handler发送过来的消息怎么对应给该Handler进行处理,靠的就是这个字段。Handler在发送消息时给Message的target赋值。即可以根据Message找到对应的Handler.

What:同一个handler可以发送很多Message,通过what来区分是哪一条消息。

when:这个是系统使用的字段。我们在得到Message对象后不能发送两次,因为系统处理后Message的when设为0,再次遇到0的Message将不处理。

二,Message类中的对象回收机制

在获取Message对象时使用的方法是:Message.obtain()。下面看下这个方法的原码:

public static Message obtain() {        synchronized (sPoolSync) {            if (sPool != null) {                Message m = sPool;                sPool = m.next;                m.next = null;                m.flags = 0; // clear in-use flag                sPoolSize--;                return m;            }        }        return new Message();    }

由原码可知,在Message类中维护了一个消息池,当消息池中有Message对象时就拿过来使用,当没有Message对象时就创建对象。其实Message有一个回收机制,当Message消息被Handler执行过后就会被回收,放到消息池中,等待下一次使用,避免了对象不停的创建和销毁。Message的回收机制主要方法是:recycle()方法和recycleUnchecked()方法,这两个方法只是Message中消息回收机制中的方法,对Handler运行机制没有作用,这儿不做介绍,感兴趣的小伙伴可以自己去看原码。

Handler类

在handler的使用方法中已经接触到了handler类的几个方法,下面对重要方法做具体分析。

一,Handler的构造方法

在创建Handler对象时一般使用的是无参构造,无参构造的原码是:

    public Handler() {        this(null, false);    }

继续往下看

    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());            }        }        mLooper = Looper.myLooper();//得到Looper对象        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;//得到MessageQueue对象        mCallback = callback;        mAsynchronous = async;    }

这段代码中有两个重要之处,就是得到了Looper对象和MessageQueue对象,这两个对象就是主线程中唯一的Looper对象和MessageQueue对象。

二,enqueueMessage方法

在发送消息时使用的是sendMessage方法,但这个方法没有具体的逻辑代码,它和其他发送消息的方法一样,最终调用的是:enqueueMessage方法,下面看enqueueMessage方法的原码。

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

这个方法其实也特别简单,就两个操作,一个是将this赋值给Message的target字段。另外一个就是调用MessageQueue的enqueueMessage方法,将Message放入到消息队列中。

三,dispatchMessage方法

这个方法的作用是分发消息,原码如下:

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

注:这个方法在Looper的loop方法中被调用,因此执行了我们重写了handleMessage方法。

MessageQueue类

MessageQueue的中文翻译是消息队列,作用是存储消息对象,里面是以单链表的结构进行存储。

MessageQueue的作用有两个,插入消息,取出并删除消息。
插入消息对应的方法是:enqueueMessage
取出并删除消息对应的方法是:next

一,enqueueMessage方法

enqueueMessage的核心代码如下:

boolean enqueueMessage(Message msg, long when) {    if (msg.target == null) {        throw new IllegalArgumentException("Message must have a target.");    }    if (msg.isInUse()) {        throw new IllegalStateException(msg + " This message is already in use.");    }    synchronized (this) {        msg.markInUse();        msg.when = when;        Message p = mMessages;        boolean needWake;        if (p == null || when == 0 || when < p.when) {            // New head, wake up the event queue if blocked.            msg.next = p;            mMessages = msg;            needWake = mBlocked;        } else {            needWake = mBlocked && p.target == null && msg.isAsynchronous();            Message prev;            msg.next = p; // invariant: p == prev.next            prev.next = msg;        }    }    return true;}

分析:这个方法的作用是存储Message对象,方法中首先做了一个判断:这个Message对象必须有target字段,即必须带有handler对象。剩下的操作就是根据时间对Message进行排序,设置message的next指向。

注:这个方法是在handler中的sendMessage方法中被调用的。

二,next方法

next方法的核心代码是:

Message next() {

for (;;) {    synchronized (this) {        // Try to retrieve the next message.  Return if found.        final long now = SystemClock.uptimeMillis();        Message prevMsg = null;        Message msg = mMessages;        if (msg != null) {            if (now < msg.when) {                // Next message is not ready.  Set a timeout to wake up when it is ready.                nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);            } else {                // Got a message.                mBlocked = false;                if (prevMsg != null) {                    prevMsg.next = msg.next;                } else {                    mMessages = msg.next;                }                msg.next = null;                if (DEBUG) Log.v(TAG, "Returning message: " + msg);                msg.markInUse();                return msg;            }        } else {            // No more messages.            nextPollTimeoutMillis = -1;        }  }

}
分析:这个方法的原码比较多,作用就是返回一个Message对象,并把该Message对象从消息队列中清除。

这个方法中维护了一个死循环,只有当有msg对象不等于null时才有返回值,否则会一直循环,UI线程的代码就停留在了这儿。

Looper类

Looper的中文翻译是循环,这里叫消息循环。MessageQueue的作用是存储消息,looper的作用就是从messageQueue里面取出新消息,然后交由handler进行处理。

一,prepareMainLooper()方法

这个方法的作用是创建UI线程中的Looper对象。原码如下:

    public static void prepareMainLooper() {        prepare(false);//创建Looper对象。        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();//得到Looper对象。        }    }

这段代码中调用了两个非常重要的方法,一个是prepare,这个方法中创建了Looper对象,并把Looper对象保存到ThreadLocal中。另外一个是myLooper方法,这个方法的作用是取出保存在ThreadLocal中的Looper对象。

二,Looper.loop()方法

这个方法是重重之重,它好比司令部,对整个消息机制的运行起支配作用。
方法的原码:

public static void loop() {    final Looper me = myLooper();//1,获取Looper对象    final MessageQueue queue = me.mQueue;    for (;;) {//开启一个死循环        Message msg = queue.next(); // 从消息队列中取出消息        if (msg == null) {            return;        }        Printer logging = me.mLogging;        if (logging != null) {            logging.println(">>>>> Dispatching to " + msg.target + " " +                    msg.callback + ": " + msg.what);        }        msg.target.dispatchMessage(msg);//调用handler的dispatchMessage方法        if (logging != null) {            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);        msg.recycleUnchecked();//回收Message对象    }}

总结来说,这个方法中做了四件大事:

  1. 得到UI线程内的looper对象和MessageQueue对象。
  2. 开启一个死循环,在死循环中不断的从MessageQueue中取消息。
  3. 取出消息后调用handler的dispatchMessage方法分发消息。
  4. 回收Message对象。


Handler机制中各方法的调用关系

Handler机制中的方法按执行的时机不同可以分为三块,一是app启动时的准备工作,二是当我们调用handler的sendMessage时发送消息,三是处理消息。

一,app启动时的准备工作

一个app启动时会调用ActivityThread类中的main方法。这个是非常重要的方法,在这里面做了很多事情,下面只看与Handler机制有关的代码:

    public static void main(String[] args) {              SamplingProfilerIntegration.start();              //。。。            Looper.prepareMainLooper();              // 创建ActivityThread实例              ActivityThread thread = new ActivityThread();              thread.attach(false);                 if (sMainThreadHandler == null) {                  sMainThreadHandler = thread.getHandler();              }              AsyncTask.init();              if (false) {                  Looper.myLooper().setMessageLogging(new                          LogPrinter(Log.DEBUG, "ActivityThread"));              }              Looper.loop();              throw new RuntimeException("Main thread loop unexpectedly exited");          }  

这个方法中调用了两个重要的 方法:
Looper. prepareMainLooper()和Looper.loop();

Looper. prepareMainLooper()的作用是:创建Looper对象和MessageQueue对象。
Looper.loop()的方法是开启死循环。代码会停留在儿,等待消息队列中的消息。

二,发送消息

发送消息使用的是Handler的sendMessage方法,最终调用的是MessageQueue的enqueueMessage方法,把消息放入消息队列。

三,处理消息

当消息队列有有消息时,Looper的loop方法就会执行,调用queue.next()得到Message对象,然后再调用handler的HandleMessage方法处理消息。

总结

看到这儿应该对Handler的执行机制都明白了,这儿再做一个总结:
在app启动时首先执行的是ActivityThread类中的main方法,这个方法中主要做两件事情,一是调用looper的prepare方法创建looper对象和Messagequeue对象。二是调用looper的loop方法,开启一个死循环不断的从消息队列中获取消息。我们使用Handler首先在主线程创建一个handler对象,并重写handlemessage方法。在子线程中使用sendMessage将消息发送到消息队列中,此时looper的loop方法就可以从消息队列中拿出消息,然后交由handler对象进行处理,然后就执行了我们重写的handlemessage方法。

0 0
原创粉丝点击