Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android中Handler机制的理解

$
0
0

一、为什么需要Handler

在早些年刚开始接触Android开发时,就曾遇到过这样一个异常,当我们在子线程中更新UI界面时会出现:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views,翻译过来就是只有创建这个控件的线程才能去更新该控件的内容。可见当年真的是too young too simple。

既然UI更新只能在创建它的UI线程中更新,那么Handler机制的出现也就理所当然了,它就是用来处理子线程更新UI线程的问题的。Handler负责与子线程进行通讯,从而让子线程与UI线程之间建立起联系,达到可以异步更新UI的效果。

二、Handler机制

说到Handler机制,又不得不提这么几个类:Message、Looper、MessageQueue、Handler

1、消息类Message

看Message类的介绍:

/**
 * 
 * Defines a message containing a description and arbitrary data object that can be
 * sent to a {@link Handler}.  This object contains two extra int fields and an
 * extra object field that allow you to not do allocations in many cases.  
 *
 * <p class="note">While the constructor of Message is public, the best way to get
 * one of these is to call {@link #obtain Message.obtain()} or one of the
 * {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
 * them from a pool of recycled objects.</p>
 */
public final class Message implements Parcelable {
    ......
}

android.os.Message的主要功能是进行消息的封装,同时可以指定消息的操作形式。使用它时有几点要注意:
- 尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源;
- 如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存;
- 擅用message.what来标识信息,以便用不同方式处理message。

2、消息循环类Looper

Android中对Looper的定义:

/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  * 
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  * 
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  */
public final class Looper {
    ......
}

由定义可知,Looper就是用来在一个Thread中进行消息的轮询,使得一个普通的线程变成Looper线程(循环工作的线程)。

通过Looper类创建一个循环线程实例:

public class LooperThread extends Thread {
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();

        // ...其他处理,如实例化handler

        // 开始循环处理消息队列
        Looper.loop();
    }
}

关键的代码就是Looper.prepare()和Looper.loop(),那么这两行代码到底做了什么呢?让我们看源码:

1. 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.prepare()可以看出,它创建了一个Looper的实例,Looper的构造函数是私有的,外部不能访问。

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

final MessageQueue mQueue;
final Thread mThread;

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

可以看到Looper的内部维护了一个消息队列MessageQueue以及一个线程对象。从以上源码可看出,一个Thread只能有一个Looper实例。这也说明Looper.prepare()方法不能被调用两次,同时一个Looper实例也只有一个MessageQueue。背后的核心就是将Looper对象定义为ThreadLocal。

2. 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();
    }
}

可以看到,当调用Looper.loop()方法后,Looper线程就开始真正工作了,会一直轮循获取消息,直到没有消息返回退出。

我们也可以从里面看到消息分发的调用msg.target.dispatchMessage(msg)。该调用表示将消息交给msg的target的dispatchMessage方法去处理,而msg的target就是我们之后要分析的Handler对象。

Looper中的myLooper()方法用于获取当前线程的Looper对象

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static Looper myLooper() {
    return sThreadLocal.get();
}

这里可能有人有疑问,为何我们没有主动执行Looper.prepare()方法,却可以在UI线程中获取到Looper.myLooper()对象呢?其实系统在启动时已经初始化了UI线程的Looper对象了,具体源码在SystemServer的run()方法及ActivityThread的main()方法中,通过执行Looper类的prepareMainLooper()来实现的:

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
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类也提供了获取UI线程Looper实例的方法了。

/** Returns the application's main looper, which lives in the main thread of the application.
 */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

这里再次重申每个线程有且只能有一个Looper对象,每个线程的Looper对象都由ThreadLocal来维持。

3、消息操作类Handler

先看下Handler类的定义:

/**
 * A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link 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.
 * 
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed as some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.
 * 
 * <p>Scheduling messages is accomplished with the
 * {@link #post}, {@link #postAtTime(Runnable, long)},
 * {@link #postDelayed}, {@link #sendEmptyMessage},
 * {@link #sendMessage}, {@link #sendMessageAtTime}, and
 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
 * you to enqueue Runnable objects to be called by the message queue when
 * they are received; the <em>sendMessage</em> versions allow you to enqueue
 * a {@link Message} object containing a bundle of data that will be
 * processed by the Handler's {@link #handleMessage} method (requiring that
 * you implement a subclass of Handler).
 * 
 * <p>When posting or sending to a Handler, you can either
 * allow the item to be processed as soon as the message queue is ready
 * to do so, or specify a delay before it gets processed or absolute time for
 * it to be processed.  The latter two allow you to implement timeouts,
 * ticks, and other timing-based behavior.
 * 
 * <p>When a
 * process is created for your application, its main thread is dedicated to
 * running a message queue that takes care of managing the top-level
 * application objects (activities, broadcast receivers, etc) and any windows
 * they create.  You can create your own threads, and communicate back with
 * the main application thread through a Handler.  This is done by calling
 * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
 * your new thread.  The given Runnable or Message will then be scheduled
 * in the Handler's message queue and processed when appropriate.
 */
 public class Handler {
    ......
 }

简单的说,Handler就是用来和一个线程对应的MessageQueue进行打交道的,起到对Message及Runnable对象进行发送和处理(只处理自己发出的消息)的作用。每个Handler实例关联了单个的线程和线程里的消息队列。

Handler创建时默认会关联一个当前线程的Looper对象,通过构造函数我们也可以将其关联到其它线程的Looper对象,先看下Handler默认的构造函数:

/**
 * 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());
        }
    }
    //默认将关联当前线程的looper
    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;
}

可以预知,当我们在主线程中创建默认的Hanler时不会有问题,而在子线程中用默认构造函数创建时一定会报错,除非我们在Handler构造前执行Looper.prepare(),生成一个子线程对应的Looper实例才行,比如:

public class LooperThread extends Thread {
    private Handler handler1;
    private Handler handler2;

    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();

        // 实例化两个handler
        handler1 = new Handler();
        handler2 = new Handler();

        // 开始循环处理消息队列
        Looper.loop();
    }
}

可以看到,一个线程是可以有多个Handler的,但是只能有一个Looper的实例

Handler的发送消息主要通过sendMessage()方法,最终调用的就是sendMessageAtTime()方法:

/**
 * 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);
}

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

其中msg.target = this就是将当前的Handler作为msg的target属性,以确保确looper执行到该message时能找到处理它的Handler对象,对应Looper.loop()方法中的

msg.target.dispatchMessage(msg);

Handler可以在任意线程发送消息,而且消息的发送方式除了有sendMessage()还有post()方法。其实post发出的Runnable对象最后都被封装成message对象了,看源码:

/**
 * 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;
    }

在开发中,我们常使用的

mHandler.post(new Runnable()  
        {  
            @Override  
            public void run()  
            {  
                //更新UI操作
            }  
        });  

其实并没有创建线程,而只是发送了一条消息而已,最后通过Message的callback方法进行了处理而已。这里就设计到Handler处理消息的机制了。

那么Handler是如何处理消息的呢?其实在前面的Looper.loop()方法中我们已经可以看到了,就是通过msg.target.dispatchMessage(msg);来实现的,通过找到发送消息的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);
    }
}

也即Handler是在它关联的Looper线程中进行处理消息的。Handler在收到消息后,先判断消息内的回调Runnable是否为空(post(Runnable)等方法),为空的话,看是否实现内部回调接口,实现了的话由回调接口回调给用户进行处理,否则由自身的handlerMessage()方法进行处理。

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

/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 *
 * @param msg A {@link android.os.Message Message} object
 * @return True if no further handling is desired
 */
public interface Callback {
    public boolean handleMessage(Message msg);
}

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

通过这样一种机制,就能解决在其他非主线程中更新UI的问题,通常的做法是:在activity中创建Handler实例并将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知activity更新UI

参考:http://my.oschina.net/u/1391648/blog/282892

作者:isee361820238 发表于2016/9/6 0:28:35 原文链接
阅读:27 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>