android Handler机制源码详解

来源:互联网 发布:打扮家 知乎 编辑:程序博客网 时间:2024/05/01 20:18

1、Handler介绍

Handler机制是android系统提供给我们的一种消息处理机制,最常见的应用就是在子线程更新UI操作。其实查看android源码可以发现,activity中所有的生命周期方法的回调都是应用的Handler机制。

Handler机制主要是为了解决安卓系统中多线程并发的问题,试想如果如果在没有加锁的情况下在多个子线程中更新主线程即UI线程界面,会造成界面错乱,我们也无法控制界面显示,如果在UI线程中加锁的话又会造成性能下降,影响用户体验。Google推出的Handler机制则不需要考虑这些问题,对UI界面的更新操作都会封装成一个个message轮询处理。

2、Message、Looper、MessageQueue

引用官方API中的描述:

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue。
大意就是每个Handler对象在创建的时候都会和对应的线程及其MessageQueue一一对应,绑定在一起。Handler会将message和runnable分发到MessageQueue中并按顺序处理。

我们平时在子线程中更新UI一般都是类似如下代码

        private TextView textView;    private Button bt_update;    Handler mHandler = new Handler(){public void handleMessage(android.os.Message msg) {String str = (String) msg.obj;textView.setText(str);};    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                textView = (TextView)findViewById(R.id.textview);        bt_update = (Button)findViewById(R.id.bt_update);        bt_update.setOnClickListener(this);    }        @Overridepublic void onClick(View v) {    switch (v.getId()) {case R.id.bt_update:new Thread(){        public void run() {        try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}        Message message = Message.obtain();        message.obj = "我更新了";        mHandler.sendMessage(message);        };        }.start();break;default:break;}}
整个过程大致如下:Message负责封装消息,Looper类负责轮询消息,相当于水泵,不停的调用下一个消息。MessageQueue是Looper的内部一个存放消息的消息队列。在handler调用sendMessage方法时会将message插入到MessageQueue中,然后调用Looper.loop()方法去轮询消息并调用handler的handleMessage方法。

3、Handler机制源码分析

我们首先查看一下Handler的构造方法:

    /**     * Default constructor associates this handler with the {@link Looper} for the     * current thread.     *     * If this thread does not have a looper, this handler won't be able to receive messages     * so an exception is thrown.     */    public Handler() {        this(null, false);    }    /**     * Constructor associates this handler with the {@link Looper} for the     * current thread and takes a callback interface in which you can handle     * messages.     *     * If this thread does not have a looper, this handler won't be able to receive messages     * so an exception is thrown.     *     * @param callback The callback interface in which to handle messages, or null.     */    public Handler(Callback callback) {        this(callback, false);    }    /**     * Use the provided {@link Looper} instead of the default one.     *     * @param looper The looper, must not be null.     */    public Handler(Looper looper) {        this(looper, null, false);    }    /**     * Use the provided {@link Looper} instead of the default one and take a callback     * interface in which to handle messages.     *     * @param looper The looper, must not be null.     * @param callback The callback interface in which to handle messages, or null.     */    public Handler(Looper looper, Callback callback) {        this(looper, callback, false);    }    /**     * Use the {@link Looper} for the current thread     * and set whether the handler should be asynchronous.     *     * Handlers are synchronous by default unless this constructor is used to make     * one that is strictly asynchronous.     *     * Asynchronous messages represent interrupts or events that do not require global ordering     * with respect to synchronous messages.  Asynchronous messages are not subject to     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.     *     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.     *     * @hide     */    public Handler(boolean async) {        this(null, async);    }    /**     * Use the {@link Looper} for the current thread with the specified callback interface     * and set whether the handler should be asynchronous.     *     * Handlers are synchronous by default unless this constructor is used to make     * one that is strictly asynchronous.     *     * Asynchronous messages represent interrupts or events that do not require global ordering     * with respect to synchronous messages.  Asynchronous messages are not subject to     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.     *     * @param callback The callback interface in which to handle messages, or null.     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.     *     * @hide     */    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();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }    /**     * Use the provided {@link Looper} instead of the default one and take a callback     * interface in which to handle messages.  Also set whether the handler     * should be asynchronous.     *     * Handlers are synchronous by default unless this constructor is used to make     * one that is strictly asynchronous.     *     * Asynchronous messages represent interrupts or events that do not require global ordering     * with respect to synchronous messages.  Asynchronous messages are not subject to     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.     *     * @param looper The looper, must not be null.     * @param callback The callback interface in which to handle messages, or null.     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.     *     * @hide     */    public Handler(Looper looper, Callback callback, boolean async) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;        mAsynchronous = async;    }
可以看出,前面的构造方法最终都调用到了最后两个,两个参数的和三个参数的,三个参数的要传入一个Looper。

我们看看最常用的无参的构造方法,它调用的是Handler(Callback callback, boolean async)这个构造方法。方法中开始即84-91行,检测Handler是否会引发内存泄漏,然后第93行调用了Looper.myLooper()并赋值给我们的成员变量mLooper,我们看一下这个方法

/**     * Return the Looper object associated with the current thread.  Returns     * null if the calling thread is not associated with a Looper.     */    public static @Nullable Looper myLooper() {        return sThreadLocal.get();    }
sThreadLocalThreadLocal的一个实例。ThreadLocal主要用来存储和当前线程相关联的对象,它会给每个使用ThreadLocal中变量的线程都保存一个副本,线程之间修改和使用该变量都是独立的,ThreadLocal常用的就是set和get方法。如果mLooper为空的话,就会抛出一个异常new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()")。这个异常什么时候会出现呢?当我们在子线程中new Handler()之前,如果没有调用Looper.prepare()时,就会抛出这个异常。那么我们看看这个Looper.prepare()究竟干了什么

/** Initialize the current thread as a looper.      * This gives you a chance to create handlers that then reference      * this looper, before actually starting the loop. Be sure to call      * {@link #loop()} after calling this method, and end it by calling      * {@link #quit()}.      */    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对象并保存在sThreadLocal中,顺便我们可以看到,一个线程中只能创建一个Looper对象,如果调用过Looper.prepare()后再次调用就会抛出异常。

平时我们在主线程中创建Handler时并没有调用Looper.prepare(),这是因为android系统在启动的时候已经在主线程创建了一个Looper,同时还调用了Looper.loop()方法去不停的轮询处理消息。但是子线程并没有这些操作,所以子线程中我们创建Handler时就要这样操作

new Thread(){public void run() {                Looper.prepare();                Handler handler = new Handler(){        public void handleMessage(Message msg) {        //do something        };        };                Looper.loop();};}.start();

Looper.loop()要和Looper.prepare()配套使用,否则handleMessage还是无法执行。我们来看看Looper.loop()方法做了什么

/**     * Run the message queue in this thread. Be sure to call     * {@link #quit()} to end the 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            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            msg.target.dispatchMessage(msg);            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();        }    }
第6-9行判断当前线程的looper是否为空,为空的话会抛出异常。如果不为空的话就将当前looper的messagequeue对象放在一个无限for循环中去轮询处理,即17-49行代码。for循环中我们重点看31行,其中msg.target就是发送该条消息的handler,至于什么时候赋值的我们看看handler的sendMessage方法

/**     * Pushes a message onto the end of the message queue after all pending messages     * before the current time. It will be received in {@link #handleMessage},     * in the thread attached to this handler.     *       * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.     */    public final boolean sendMessage(Message msg)    {        return sendMessageDelayed(msg, 0);    }
直接调用了sendMessageDelayed,我们继续看

/**     * Enqueue a message into the message queue after all pending messages     * before (current time + delayMillis). You will receive it in     * {@link #handleMessage}, in the thread attached to this handler.     *       * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.  Note that a     *         result of true does not mean the message will be processed -- if     *         the looper is quit before the delivery time of the message     *         occurs then the message will be dropped.     */    public final boolean sendMessageDelayed(Message msg, long delayMillis)    {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);    }    /**     * Enqueue a message into the message queue after all pending messages     * before the absolute time (in milliseconds) <var>uptimeMillis</var>.     * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>     * Time spent in deep sleep will add an additional delay to execution.     * You will receive it in {@link #handleMessage}, in the thread attached     * to this handler.     *      * @param uptimeMillis The absolute time at which the message should be     *         delivered, using the     *         {@link android.os.SystemClock#uptimeMillis} time-base.     *              * @return Returns true if the message was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.  Note that a     *         result of true does not mean the message will be processed -- if     *         the looper is quit before the delivery time of the message     *         occurs then the message will be dropped.     */    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(),然后在其中判断mQueue是否为空,这个mQueue就是我们在Handler的构造方法中赋值的。如果mQueue不为空就会调用enqueueMessage,我们看看这个方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }
原来在这里就将handler赋值给了msg.target,最后还调用queue.enqueueMessage将消息和时间都插入到MessageQueue中等待Looper轮询处理。ok,那么handler的handleMessage方法是什么时候调用的呢?我们返回到Looper.loop()方法中,我们看看for循环中的msg.target.dispatchMessage(msg)这句,这里我们已经知道msg.target就是对应的handler,我们看看handler的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是否为空,这个值我们一般没有赋值。如果为空的话就会判断mCallback是否为空,这个mCallback是什么赋值的呢?其实也是在创建handler的时候。如果我们创建handler的时候没有创建mCallback,就会直接执行handleMessage方法;否则就会执行mCallback的handleMessage方法,这个方法有个boolean型返回值,返回值为true时就不会执行handler自身的handleMessage方法,为false时就会执行自身的handleMessage方法。

至此整个流程也就结束。


4、更新UI的四种方式

第一种handler.post(Runnable r)

/**     * Causes the Runnable r to be added to the message queue.     * The runnable will be run on the thread to which this handler is      * attached.      *       * @param r The Runnable that will be executed.     *      * @return Returns true if the Runnable was successfully placed in to the      *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.     */    public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }
    private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;    }
view.post(Runnable action)
/**     * <p>Causes the Runnable to be added to the message queue.     * The runnable will be run on the user interface thread.</p>     *     * @param action The Runnable that will be executed.     *     * @return Returns true if the Runnable was successfully placed in to the     *         message queue.  Returns false on failure, usually because the     *         looper processing the message queue is exiting.     *     * @see #postDelayed     * @see #removeCallbacks     */    public boolean post(Runnable action) {        final AttachInfo attachInfo = mAttachInfo;        if (attachInfo != null) {            return attachInfo.mHandler.post(action);        }        // Assume that post will succeed later        ViewRootImpl.getRunQueue().post(action);        return true;    }
runOnUiThread(Runnable action),这个方法是Activity中的方法

/**     * Runs the specified action on the UI thread. If the current thread is the UI     * thread, then the action is executed immediately. If the current thread is     * not the UI thread, the action is posted to the event queue of the UI thread.     *     * @param action the action to run on the UI thread     */    public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }

用说了,就是我们最常用的方式:handler.sendMessage(Message msg)


以上四种方式,其实查看源码发现最后都回归到了handler机制上,所以理解了handler机制就可以融会贯通了。








        

2 0
原创粉丝点击