Looper、Handler的关系(三)

Looper、Handler的关系(二)


在前一节中讨论了Looper类中的两个主要方法,也分析了Looper、Message和Handler的关系。那么下面将分析Handler这个类。

一、先看看Handler类中的几个变量和几个构造函数

    final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;
       上面三个变量,在Handler中,也有一个消息队列mQueue;一个Looper,还有一个回调的类Callback.。这三个变量是怎么初始化的呢,那么就要看Handler的构造函数了。Handler总共有4个构造函数,以下是代码:

  /**
     * Default constructor associates this handler with the queue for the
     * current thread.
     *
     * If there isn't one, this handler won't be able to receive messages.
     */
    public Handler() {
        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;//得到Looper的消息队列。
        mCallback = null;
    }

    /**
     * Constructor associates this handler with the queue for the
     * current thread and takes a callback interface in which you can handle
     * messages.
     */
    public Handler(Callback callback) {
        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;
    }

    /**
     * Use the provided queue instead of the default one.
     */
    public Handler(Looper looper) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = null;
    }

    /**
     * Use the provided queue instead of the default one and take a callback
     * interface in which to handle messages.
     */
    public Handler(Looper looper, Callback callback) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
    }
     上面四个构造函数已经说明了,Looper就是当前调用线程的looper,mQueue也是Looper中的消息队列。那么Handler这样做的真正原因呢?


二、Handler的原理

      假设一个问题,当我们要往Looper的消息队列中插入消息时,我们应该怎么做呢?如果没有Handler,我们可能会这样做:

      1、获取Looper中的消息队列mQueue,也就是Looper中的myQueue()方法,得到的是MessageQueue对象;

      2、构造一个Message对象,将相应的变量复制,特别是将里面的target变量设为处理消息的Handler;

      3、将上面的message对象,插入到上面的mQueue队列中。

    基本的原理也就如此,但是这样却有一些麻烦,也会甙类一些不确定的错误。而引入Handler,就让这些工作变得简单了。通过Handler中的其他方法就能看到,Handler提供了一些列方法,帮助我们完成创建消息,插入消息的工作,下面是部分方法:

  public final boolean hasMessages(int what) //查看当前消息队列中是否有消息码为what的消息。
  public final Message obtainMessage(int what)//从Handler中创建一个消息码为what的消息
  public final void removeMessages(int what)//从消息队列中,移除消息码为what的消息
  public final boolean sendEmptyMessage(int what) //发送一个只有消息码what的空消息
  public final boolean sendMessage(Message msg)//发送一个消息,该消息将被添加到队尾
  public final boolean sendMessageAtFrontOfQueue(Message msg)//发送一个消息,将消息添加到队头
       上面只列了Handler中的部分方法,稍后会在附件中附上Handler的源码。

       对于几个方法,只要看了源码,就能明白其他函数的意思了。

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

    /**
     * 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>
     * 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)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;//将消息的target设为自己,然后将消息加入到消息队列中。
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

       通过上面的部分方法,就能明白Handler的工作实际是辅助的,将对MessageQueue的操作封装起来。这里处理对内,将消息封装、添加队列,还提供了对外处理消息的接口。这就是我们经常在处理消息时,需要重写Handler的handleMessage方法的地方。

三、Handler的消息处理

       在之前,我们看过Looper的方法,发现Looper在处理消息的时候,会调用target的dispatchMessage方法,就等于是把消息派发给Handler处理。那么Handler中的dispatchMessage又是怎样执行呢?

 
    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {

        if (msg.callback != null) {//如果Message有callback,则直接交给callback处理。
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {//如果handler中设置了callback,则直接交给handler的callback处理。
                    return;
                }
            }
            handleMessage(msg);//最后交给子类处理。
        }
    }
    在dispatchMessage中,定义了一套处理消息的优先机制:

    1、Message如果自带了callback,则直接交由callback处理;

    2、Handler如果设置了全局的mCallback,则交给mCallback处理;

    3、当上述都没有处理的时候,该消息则直接交给Handler的子类来处理。这就是为什么在我们自己的代码中,通常要派生出一个Handler,然后重写HandleMessage方法。这是平常用得最多的方式。

    

 四、Looper和Handler的同步关系

      Looper和Handler是有同步关系的,这与多线程有关。举个例子:

     

//定义一个LooperThread类
class LooperThread extends Thread {
         public Looper myLooper = null;
         public void run() {//假设run()在线程2中执行
                Looper.prepare();
                //myLooper必须在这个线程中赋值。
                myLooper = Looper.myLooper();
                Looper.loop();
         }
}

//下面的代码在线程1中执行,并且会创建线程2;
{
   LooperThread lpThread = new LooperThread();
   lpThread.start();//创建线程2
   Looper looper = lpThread.myLooper;//获取线程2中的looper ***********
   
   Handler thread2Handler = new Handler(looper);//利用线程2中的looper创建线程1中的Handler,将Handler与线程2中的looper关联。
   thread2Handler.sendMessage(...);//发送消息,将交给线程2来处理。
} 
      上面的代码比较简单:

      1、线程1中创建线程2,并且线程2通过Looper处理消息。

      2、线程1中得到线程2中的looper,并且根据这个looper创建一个Handler,这样发送给该Handler的消息酱油线程2处理。

      理解比较简单,但是这个地方时有问题的。在多线程中,myLooper的创建是在线程2中,而looper的赋值却是在线程1中,有可能出现,线程2的run方法还没有执行,也就是说还没有光剑myLooper,那么这时候给线程1中的looper赋值,就会变成null。

另外,

       Handler thread2Handler = new Handler(looper); 不能替换成

       Handler thread2Handler = new Handler(Looper.myLooper());

这是由于,myLooper()返回的是调用线程的looper,也就是线程1的Looper,而不是线程2的looper;

      上面的问题就需要采用同步的方式来处理。在Android中,提供了一个HandlerThread来解决这个问题。


五、HandlerThread

      HandlerThread完美的解决了上面的问题,处理了myLooper可能为空的问题。以下是HandlerThread的代码:

/**
 * 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 {
    private int mPriority;
    private int mTid = -1;
    private 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 over ridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

//线程2运行它的run函数,looper就是在run线程中创建
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();//创建这个线程上的looper
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();//通知取looper的线程1,此时looper已经创建好了。
        }
        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() {//线程1调用getLooper来获得新线程的Looper
        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();//如果新线程还未创建Looper,则等待
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    
    /**
     * Ask the currently running looper to quit.  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.
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    
    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}
     HandlerThread很简单,就是使用了同步中的wait和notifyAll就解决了。所以在使用的时候,我们可以多使用HandlerThread,而避免出现同步的问题。
     总结一下:Handler会关联一个单独的线程和消息队列。Handler默认关联主线程,虽然要提供Runnable参数 ,但默认是直接调用Runnable中的run()方法。也就是默认下会在主线程执行,如果在这里面的操作会有阻塞,界面也会卡住。

                        如果要在其他线程执行,可以使用HandlerThread。HandlerThread的简单用法:

HandlerThread uIhandlerThread = new HandlerThread("update");
uIhandlerThread.start();
Handler uIhandler = new Handler(uIhandlerThread.getLooper(),new Callback() {
    public boolean handleMessage(Message msg) {
        Bundle b = msg.getData();  
        int age = b.getInt("age");  
        String name = b.getString("name");  
        System.out.println("age is " + age + ", name is" + name);  
        System.out.println("Handler--->" + Thread.currentThread().getId());  
        System.out.println("handlerMessage");  
        return true;  
     }
});

这样,消息都将发到HandlerThread所在的线程中去处理。HandlerThread一般与Handler配合完成。

最后,将Looper,Handler的关系理清了,里面也有一些其他类,包括HandlerThread和Message,具体的使用以及注意,需要再实际的使用中才能明白,但是,先理清几个类之间的关系,可以避免不必要的问题。


附上几个类的下载地址:

下载地址


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值