Android 之 Handler 使用

            Handler是Android系统中重要的组成部分,不管是Android的原生代码中,还是我们在开发应用程序的过程中,都会大量使用Handler,来进行消息的处理。要想更好的使用Handler,就发须理解Android的Handler消息处理机制。详细的Handler消息处理机制,请移步Android应用程序消息处理机制(Looper、Handler)分析 

           在这里,从如下几个方面去了解Handler。

  • Handler的整体架构
  • Handler中SendMessage、PostMessage两者的联系及区别
  • 如何在一个线程中使用Handler来处理消息


        一、Handler的整体架构

         在Handler的消息处理机制中,有几个非常重要的类,分别是:Handler、Looper、MessageQueue、Message。

1. Handler: 用于发送和接收并处理消息。

2. Looper: 用于不断的从MessageQueue中取出消息。

3. MessageQueue: 用于装载消息。

4. Message: 消息的实体。


二、Handler中sendMessage、postMessage两者的联系及区别

要弄清楚sendMessage、postMessage这两者的联系及区别,我们就先来看看都有哪些方法吧。


Handler中所有的send方法



Handler中所有的post方法


从Handler的send和post的这些方法中,给我们一个直观的区别是:

send都是很明确的发送一个Message;post并没有看出向外发送Message,而是在post的同时可以处理Runnable。


为了看清楚这两种发送消息的本质,我们就去看看Handler的这几个发送消息的方法源码吧。

先从send的看起,就以sendEmptyMessage来说吧,方法如下:

public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

sendEmptyMessage方法中又调用了sendEmptyMessageDelayed(int what, long delayMillis)方法,

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
sendEmptyMessageDelayed方法中又调用了sendMessageDelayed(Message msg, long delayMillis)方法,
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
sendMessageDelayed方法中又调用了sendMessageAtTime(Message msg, long uptimeMillis)方法,
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);
}
到此,整个发送过程就完成了。


现在再来看看post的过程,那我们就分别看看post的几个方法都是怎样处理的。

post(Runnable r)

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

postAtFrontOfQueue(Runnable r)

public final boolean postAtFrontOfQueue(Runnable r)
{
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}

postAtTime(Runnable r, long uptimeMillis)

public final boolean postAtTime(Runnable r, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}

postAtTime(Runnable r, Object token, long uptimeMillis)

public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}

postDelayed(Runnable r, long delayMillis)

public final boolean postDelayed(Runnable r, long delayMillis)
{
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

看了post的几个方法后,我们就很清楚的知道,最后还是调用了send的对应方法。


小结:

区别:sendMessage方法会直接发送Message,而postMessage在发送Message的同时还可以处理线程。

联系:

1. sendMessage和postMessage都可以发送消息。

2. postMessage的方法最后都会调用sendMessage的方法。


三、如何在一个子线程中创建Handler对象

要使用Handler,则必须先创建一个Handler的对象,并重写Handler类中的handleMessage()方法,通常我们在主线程中的创建方法如下:

Handler handler = new Handler() {
    public void handleMessage(Message msg) {
			
    };
};


而如果我们要在一个子线程中,使用上面的这种方式创建Handler对象,如下:

class SubThread implements Runnable {
    public Handler mHandler;
    @Override
    public void run() {
	mHandler = new Handler() {
	    @Override
	    public void handleMessage(Message msg) {
		super.handleMessage(msg);
	    }
	};
    }
}

按照上面的方式创建Handler,行不行呢?答案是:不行。如果我们在子线程的run()方法中使用上面的创建方式,就会抛出如下异常:

08-25 19:18:22.564: E/AndroidRuntime(27359): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
这个异常信息告诉我们:如果没有事先调用Looper.perpare(),就不能在子线程程中创建Handler对象。

从这个异常信息已经很明确的告诉了我们:


方法一: 在子线程中创建Handler对象,就在new Handler()的代码行前面先调用Looper.perpare()方法。


为什么在run()方法中调用了Looper.perpare()方法后,就可以正常创建Handler对象了呢?我们就去看看这个方法都做了哪里操作吧

<span style="font-size:14px;">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));
}

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

再来看看Looper中都定义了哪些成员变量呢?

<span style="font-size:14px;">// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class

final MessageQueue mQueue;
final Thread mThread;</span>
看了这些后,就应该明白为什么在子线程中一定要在创建Handler对象的前面显示调用Looper.prepare()的原因了吧。就是为了创建一个Looper,并与前面的线程进行关联起来,这样才可以进行消息的处理。


除了显示调用Looper.prepare(),还有没有其他的方法可以在子线程中创建Handler对象呢?我们就先来看看Handler类的构造方法都有哪些呢?


从Handler的几个构造方法中,我们可以使用Handler(Looper looper)来创建Handler对象,那问题是,这里所使用的Looper是从哪里来的呢?这个Looper对象实例又是在哪个地方和线程关联上了呢?HandlerThread就是用来解决这两个问题的,我们就来看看HandlerThread的源代码吧。

<span style="font-size:14px;">**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * Quits the handler thread's looper.
     * <p>
     * Causes the handler thread's looper to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p class="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     *
     * @see #quitSafely
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * Quits the handler thread's looper safely.
     * <p>
     * Causes the handler thread's looper to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * Pending delayed messages with due times in the future will not be delivered.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p>
     * If the thread has not been started or has finished (that is if
     * {@link #getLooper} returns null), then false is returned.
     * Otherwise the looper is asked to quit and true is returned.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}</span>
从HandlerThread的源码,我们知道:

1. HandlerThread本身就是一个线程。

2.在HandlerThread的run()方法中调用了Looper.prepare()方法,并把Looper实例赋值给了HandlerThread中的成员变量mLooper。

3.调用HandlerThread的getLooper()方法就可以获取到了已初始成功的Looper对象实例。


从上面的这些分析,我们就可以知道了在子线程中创建Handler对象实例的另一种方法。

方法二:启动HandlerThread线程,调用HandlerThread.getLooper(),再调用Handler(Looper) 构造方法。

使用Handler(Looper)创建Handler的示例代码如下:

<span style="font-size:14px;">class SubThread2 implements Runnable {
	public Handler mHandler;
	@Override
	public void run() {
		HandlerThread t = new HandlerThread("");
		t.start();
		final Looper looper = t.getLooper();
		mHandler = new Handler(looper) {
			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
			}
		};
	}
}</span>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值