Handler的运行机制

概述

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方法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值