深入解析Android中Handler消息机制

来源:互联网 发布:淘宝网企业店铺申请 编辑:程序博客网 时间:2024/05/29 11:31

Android提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。Handler消息机制可以说是Android系统中最重要部分之一,所以,本篇博客我们就来深入解析Android中Handler消息机制。

Handler的简单使用

为什么系统不允许子线程更新UI

因为的UI控件不是线程安全的。
如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那为什么不对UI控件的访问加上上锁机制呢?因为有这么两个缺点:
1.上锁会让UI控件变得复杂和低效
2.上锁后会阻塞某些进程的执行
对于手机系统来说,这两个缺点是不可接受的,所以最简单高效的方法就是 —— 采用单线程模型来处理UI操作。
对开发者而言也不是很麻烦,只是通过Handler切换一下访问的线程的就好。

Handler的简单使用

既然子线程不能更改界面,那么我们现在就借助Handler让我们更改一下界面:
主要步骤是这样子的:
1.new出来一个Handler对象,复写handleMessage方法
2.在需要执行更新UI的地方 sendEmptyMessage 或者 sendMessage
3.在handleMessage里面的switch里面case不同的常量执行相关操作

public class MainActivity extends ActionBarActivity {    private TextView mTextView;    private Handler mHandler;    private static final int UI_UPDATE1 = 0;    private static final int UI_UPDATE2 = 1;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mHandler = new Handler() {            @Override            public void handleMessage(Message msg) {                switch (msg.what) {                case UI_UPDATE1:                    mTextView.setText("通过Handler方法1修改UI");                    break;                case UI_UPDATE2:                    mTextView.setText("通过Handler方法2修改UI");                    break;                }            }        };        mTextView = (TextView) findViewById(R.id.textview);        mTextView.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Log.d("Test", "点击文字");                updateUi();            }        });    }    protected void updateUi() {        new Thread(new Runnable() {            @Override            public void run() {                // 方式一和方式二可以达到相同的效果,就是更改界面                //方式一                //mHandler.sendEmptyMessage(UI_UPDATE1);                //方式二                Message msg = Message.obtain();                msg.what = UI_UPDATE2;                mHandler.sendMessage(msg);            }        }).start();    }}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.handlerdemo3.MainActivity" >    <TextView        android:id="@+id/textview"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/hello_world"        android:layout_centerInParent="true"        android:clickable="true" /></RelativeLayout>

消息机制的分析理解

安卓的异步消息处理机制就是handler机制。
主线程,ActivityThread被创建的时候就会创建Looper
Looper被创建的时候创建MessageQueue。
也就是说主线程会直接或间接创建出来Looper和MessageQueue。
Handler的工作机制简单来说是这样的
1.Handler发送消息仅仅是调用MessageQueue的enqueueMessage向插入一条信息到MessageQueue
2.Looper不断轮询调用MeaasgaQueue的next方法
3.如果发现message就调用handler的dispatchMessage,dispatchMessage被成功调用,接着调用handlerMessage()

这里写图片描述

Handler消息机制的源码分析

ThreadLocal工作原理
首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。

另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。

如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。

我们看一下ThreadLocal的set方法

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

查看values方法里面做了什么

Values values(Thread current) {        return current.localValues;}

通过上面代码我们可以知道,当values为空的时候,才调用initializeValues方法进行初始化,查看inheritValues相关逻辑:

private void inheritValues(Values fromParent) {            // Transfer values from parent to child thread.            Object[] table = this.table;            for (int i = table.length - 2; i >= 0; i -= 2) {                Object k = table[i];                if (k == null || k == TOMBSTONE) {                    // Skip this entry.                    continue;                }                // The table can only contain null, tombstones and references.                Reference<InheritableThreadLocal<?>> reference                        = (Reference<InheritableThreadLocal<?>>) k;                // Raw type enables us to pass in an Object below.                InheritableThreadLocal key = reference.get();                if (key != null) {                    // Replace value with filtered value.                    // We should just let exceptions bubble out and tank                    // the thread creation                    table[i + 1] = key.childValue(fromParent.table[i + 1]);                } else {                    // The key was reclaimed.                    table[i] = TOMBSTONE;                    table[i + 1] = null;                    fromParent.table[i] = TOMBSTONE;                    fromParent.table[i + 1] = null;                    tombstones++;                    fromParent.tombstones++;                    size--;                    fromParent.size--;                }            }        }

其实就是各种赋值table数组,进行初始化
最后才是调用values.put(this, value)把ThreadLocal和value一起保存,我们可以看一下values.put(this, value)方法

void put(ThreadLocal<?> key, Object value) {            cleanUp();            // Keep track of first tombstone. That's where we want to go back            // and add an entry if necessary.            int firstTombstone = -1;            for (int index = key.hash & mask;; index = next(index)) {                Object k = table[index];                if (k == key.reference) {                    // Replace existing entry.                    table[index + 1] = value;                    return;                }                if (k == null) {                    if (firstTombstone == -1) {                        // Fill in null slot.                        table[index] = key.reference;                        table[index + 1] = value;                        size++;                        return;                    }                    // Go back and replace first tombstone.                    table[firstTombstone] = key.reference;                    table[firstTombstone + 1] = value;                    tombstones--;                    size++;                    return;                }                // Remember first tombstone.                if (firstTombstone == -1 && k == TOMBSTONE) {                    firstTombstone = index;                }            }        }

可以看出,Threadlocal的值在table数组的存储位置总是reference的下一个位置.

接下来,查看Threadlocal的get方法

这里写图片描述

Get方法的逻辑是:通过values方法取出当前线程的localValues对象,如果为null,就返回初始值。如果localValues不为null,取出其table数组,如果reference等于table数组index角标的值,就在table[index + 1]取出其Threadlocal值。

MessageQueue工作原理

MessageQueue中文翻译就是消息队列,它内部存储了一组信息,存放的是Message,以队列的形式对外提供了插入和删除的工作(虽然名字叫做队列,但是其内部的 存储结构是单链表)
主要 插入 和 读取 两个操作,这两个操作对应着两个方法:

插入(入队) enqueueMessage(Message msg, long when)读取(出队) next()

查看enqueueMessage的源码:

boolean enqueueMessage(Message msg, long when) {        if (msg.isInUse()) {            throw new AndroidRuntimeException(msg + " This message is already in use.");        }        if (msg.target == null) {            throw new AndroidRuntimeException("Message must have a target.");        }        boolean needWake;        synchronized (this) {            if (mQuiting) {                RuntimeException e = new RuntimeException(                        msg.target + " sending message to a Handler on a dead thread");                Log.w("MessageQueue", e.getMessage(), e);                return false;            }            msg.when = when;            Message p = mMessages;            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 {                // Inserted within the middle of the queue.  Usually we don't have to wake                // up the event queue unless there is a barrier at the head of the queue                // and the message is the earliest asynchronous message in the queue.                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                for (;;) {                    prev = p;                    p = p.next;                    if (p == null || when < p.when) {                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // invariant: p == prev.next                prev.next = msg;            }        }        if (needWake) {            nativeWake(mPtr);        }        return true;

从enqueueMessage的实现来看,主要操作就是单链表的插入操作,接下来查看next方法的实现:

Message next() {        // Return here if the message loop has already quit and been disposed.        // This can happen if the application tries to restart a looper after quit        // which is not supported.        final long ptr = mPtr;        if (ptr == 0) {            return null;        }        int pendingIdleHandlerCount = -1; // -1 only during first iteration        int nextPollTimeoutMillis = 0;        for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            nativePollOnce(ptr, nextPollTimeoutMillis);            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 && msg.target == null) {                    // Stalled by a barrier.  Find the next asynchronous message in the queue.                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                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;                }                // Process the quit message now that all pending messages have been handled.                if (mQuitting) {                    dispose();                    return null;                }                // If first time idle, then get the number of idlers to run.                // Idle handles only run if the queue is empty or if the first message                // in the queue (possibly a barrier) is due to be handled in the future.                if (pendingIdleHandlerCount < 0                        && (mMessages == null || now < mMessages.when)) {                    pendingIdleHandlerCount = mIdleHandlers.size();                }                if (pendingIdleHandlerCount <= 0) {                    // No idle handlers to run.  Loop and wait some more.                    mBlocked = true;                    continue;                }                if (mPendingIdleHandlers == null) {                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];                }                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);            }            // Run the idle handlers.            // We only ever reach this code block during the first iteration.            for (int i = 0; i < pendingIdleHandlerCount; i++) {                final IdleHandler idler = mPendingIdleHandlers[i];                mPendingIdleHandlers[i] = null; // release the reference to the handler                boolean keep = false;                try {                    keep = idler.queueIdle();                } catch (Throwable t) {                    Log.wtf(TAG, "IdleHandler threw exception", t);                }                if (!keep) {                    synchronized (this) {                        mIdleHandlers.remove(idler);                    }                }            }            // Reset the idle handler count to 0 so we do not run them again.            pendingIdleHandlerCount = 0;            // While calling an idle handler, a new message could have been delivered            // so go back and look again for a pending message without waiting.            nextPollTimeoutMillis = 0;        }    }

从上面代码可以得出以下结论:
1.next方法是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞
2.当有新消息到来,next方法会返回这条消息,并将其从单链表中移除。

Looper的工作原理

Looper是一个轮询器,它的作用不断轮询MessageQueue,当如果有新的消息就交给Handler处理,如果轮询不到新的消息,那就自身就处于阻塞状态。

查看Looper类的源码,可以发现的他的构造方法里面创建了一个MessageQueue,然后将当前线程的对象保存起来

private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }

在Looper的构造方法中初始化了一个消息队列MessageQueue和一个线程Thread。从这可看出:一个Looper对应着一个消息队列以及当前线程。
当收到消息Message后系统会将其存入消息队列中等候处理。至于Looper,它在Android的消息机制中担负着消息轮询的职责,它会不间断地查看MessageQueue中是否有新的未处理的消息;若有则立刻处理,若无则进入阻塞。

相信大家一定有遇到过,在子线程中创建Handler会报如下错误
这里写图片描述
解决办法就是new Handler的时候加上Looper.prepare();
而Looper.prepare()的内部实现逻辑就是创建一个Looper

public static void prepare() {        prepare(true);    }    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));    }

线程默认是没有Looper的,但是为什么在主线程没有创建的Looper就可以使用Handler?主线程是特别的。主线程,也就是ActivityThread,当主线程被创建的时候,会调用Looper内的prepareMainLooper方法,创建Looper,该方法是专门给主线程创建Looper用的。也正因为这点,所以我们在主线程创建了Handler就直接能用了。

 public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }

由于这个特殊性,Looper还提供了一个getMainLooper方法,使得可以在任何地方获取主线程的Looper。

public static Looper getMainLooper() {        synchronized (Looper.class) {            return sMainLooper;        }    }

接下来,我们看一下Looper的退出

Looper提供了两个方法进行退出操作,分别是quit和quitSafely,他们调用的是MessageQueue的quit方法

public void quit() {        mQueue.quit(false);    }
public void quitSafely() {        mQueue.quit(true);    }

MessageQueue的quit方法:

 void quit(boolean safe) {        if (!mQuitAllowed) {            throw new IllegalStateException("Main thread not allowed to quit.");        }        synchronized (this) {            if (mQuitting) {                return;            }            mQuitting = true;            if (safe) {                removeAllFutureMessagesLocked();            } else {                removeAllMessagesLocked();            }            // We can assume mPtr != 0 because mQuitting was previously false.            nativeWake(mPtr);        }    }

如果调用Looper.quit方法,最终会调用removeAllMessagesLocked方法,该方法逻辑:直接遍历所有的消息,并将消息强制回收

   private void removeAllMessagesLocked() {        Message p = mMessages;        while (p != null) {            Message n = p.next;            p.recycleUnchecked();            p = n;        }        mMessages = null;    }

如果调用Looper.quitSafely方法,最终会调removeAllFutureMessagesLocked方法,该方法逻辑:

void removeCallbacksAndMessages(Handler h, Object object) {        if (h == null) {            return;        }        synchronized (this) {            Message p = mMessages;            // Remove all messages at front.            while (p != null && p.target == h                    && (object == null || p.obj == object)) {                Message n = p.next;                mMessages = n;                p.recycleUnchecked();                p = n;            }            // Remove all messages after front.            while (p != null) {                Message n = p.next;                if (n != null) {                    if (n.target == h && (object == null || n.obj == object)) {                        Message nn = n.next;                        n.recycleUnchecked();                        p.next = nn;                        continue;                    }                }                p = n;            }        }    }

一个无限for循环,只有n.when > now和n == null才会跳出循环,说明是等消息队列中已有的消息处理完毕后,才会跳出,然后执行回收。

Looper这个类里面最重要的方法就是loop()开启消息循环这个方法了,看一下loop代码的实现逻辑:

public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final MessageQueue queue = me.mQueue;        // Make sure the identity of this thread is that of the local process,        // and keep track of what that identity token actually is.        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            // This must be in a local variable, in case a UI event sets the logger            final Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            final long traceTag = me.mTraceTag;            if (traceTag != 0) {                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));            }            try {                msg.target.dispatchMessage(msg);            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // Make sure that during the course of dispatching the            // identity of the thread wasn't corrupted.            final long newIdent = Binder.clearCallingIdentity();            if (ident != newIdent) {                Log.wtf(TAG, "Thread identity changed from 0x"                        + Long.toHexString(ident) + " to 0x"                        + Long.toHexString(newIdent) + " while dispatching to "                        + msg.target.getClass().getName() + " "                        + msg.callback + " what=" + msg.what);            }            msg.recycleUnchecked();        }    }

通过代码我们知道:looper方法是一个死循环,唯一跳出的循环的方式是MessageQueue的next方法返回null,但这几乎不可能,因为在MessageQueue的next方法中,假如没有消息加入队列,next方法会一直阻塞,不会返回null。如果我们不手动调用quit或者quitSafely方法的话,MessageQueue的next方法是不可能返回null的。
当MessageQueue没有消息时,next方法会一直阻塞在那里,因为MessageQueue的next方法阻塞了,就导致Looper的loop方法也一直在阻塞了。
这里我们那一分为二的谈,
loop轮询不到消息:那么处于阻塞状态,然后就没有然后了,除了又轮询到了新的消息
loop轮到了新的消息:Looper就会处理消息
1、msg.target.dispatchMessage(msg),这里的 msg.target就是指Handler对象
2、到了最后,Handler发送的消息又交给了自己的dispatchMessage方法来处理了。(这个dispatchMessage方法不是Handler自己调用时,是与Handler相相关的Looper间接调用的),这样下来,就成功地将逻辑切换到指定的线程当中去了

Handler的工作原理
Handler的主要工作:消息的 发送 和 接收 。Handler消息发送的形式有post和send两种。
查看Handler中sendMessage代码

public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }

sendMessage调用sendMessageDelayed

public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }

sendMessageDelayed调用sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        MessageQueue queue = mQueue;        if (queue == null) {            RuntimeException e = new RuntimeException(                    this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);            return false;        }        return enqueueMessage(queue, msg, uptimeMillis);    }

sendMessageAtTime调用enqueueMessage

  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }
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) {            if (mQuitting) {                IllegalStateException e = new IllegalStateException(                        msg.target + " sending message to a Handler on a dead thread");                Log.w(TAG, e.getMessage(), e);                msg.recycle();                return false;            }            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 {                // Inserted within the middle of the queue.  Usually we don't have to wake                // up the event queue unless there is a barrier at the head of the queue                // and the message is the earliest asynchronous message in the queue.                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                for (;;) {                    prev = p;                    p = p.next;                    if (p == null || when < p.when) {                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // invariant: p == prev.next                prev.next = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                nativeWake(mPtr);            }        }        return true;    }

得出结论:Handler的发送消息仅仅是调用MessageQueue的enqueueMessage向插入一条信息到MessageQueue,MessageQueue就会返回这条消息给Looper,Looper会交给msg.target.dispatchMessage(msg)方法处理,就进入消息处理阶段。

查看dispatchMessage方法

/**     * Handle system messages here.     */    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

首先,判断 Message. Callback是否为null,不为null,就调用handleCallback方法

private static void handleCallback(Message message) {        message.callback.run();    }

Callback是一个Runnable对象,实际上就是post方法传递的Runnable参数。
其次,检查mCallback是否为null,如果不为null就调用mCallback.handleMessage方法,查看mCallback.handleMessage方法

 public interface Callback {        public boolean handleMessage(Message msg);    }

源码中注释已经对Callback进行了解释:
可以用来创建一个Handler的实例但不需要派生Handler的子类
在日常开发中,创建Handler最常见的方式就是派生一个Handler的子类并重写handleMessage方法来处理具体的消息,而Callback给我们提供了另外一种方式,不需要派生Handler的子类。

最后,调用Handler的handleMessage方法,这就是我们平时写Handler要实现的方法
这里写图片描述

主线程的消息循环
Android的主线程就是ActivityThread,主线程的路口方法是main方法,在 main中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue, 并通过Looper.loop()开启主线程消息循环

public static void main(String[] args) {        SamplingProfilerIntegration.start();        // CloseGuard defaults to true and can be quite spammy.  We        // disable it here, but selectively enable it later (via        // StrictMode) on debug builds, but using DropBox, not logs.        CloseGuard.setEnabled(false);        Environment.initForCurrentUser();        // Set the reporter for event logging in libcore        EventLogger.setReporter(new EventLoggingReporter());        Security.addProvider(new AndroidKeyStoreProvider());        Process.setArgV0("<pre-initialized>");        Looper.prepareMainLooper();        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");    }

主线程开始循环后,ActivityThread还需要一个Handler来和消息队列进行交互,这个Handler就是ActivityThread.H

这里写图片描述

ApplicationThread通过binder与Ams通信,并将Ams的调用,通过H类(也就是Hnalder)将消息发送到消息队列,然后进行相应的操作,H收到消息后,就会将ApplicationThread中逻辑切换到ActivityThread中执行,也就是主线程中执行,这个过程就是主线程的消息循环。

至此,Handler消息机制就分析完毕,如有错漏,欢迎留言指证。

7 0