Android中 handler , looper 以及 message 的关系

声明:


    在我的工作中用到了handler,looper 以及 message的关系,所以我在这里总结一下自己对他们关系的了解,同时也会在后面介绍如何使用他们来完成自己的任务。同时还是强调,这是我自己的一些工作总结,可能有不完善的地方, 请大家指正,希望对你有所帮忙。同时这里面有很多的looper,handler,以及message等,其实他们的首字母是大写的,只是我这里为了方便就使用小写了,所以你看的时候需要自己理解下。同时我这里有的时候会称呼方法为函数,这是我在c中习惯的称呼,希望你们不要介意

一. handler,looper,MessageQueue等的关系介绍


    在开始讲解之前,我希望大家可以对这几个名词有个全局的概念或者可以清楚他们的关系,所以使用下图来表示:

    上面的图片是我看文章的时候发现的一个很好的图示,我也会在这篇文章的参考文章中列出这位大神的博客,讲的非常详细大家可以去看一下。这里我就使用这个图来解释一下他们的关系,从上面的图中我们可以看出:线程(thread),Looper以及MessageQueue是一一对应的。也就是说,我们创建一个线程:thread,那么他有且只能有一个Looper与之对应,而一个looper有且只有一个MessageQueue与之对应。而在上面的图中显示handler与thread,looper等的关系是调用操作的关系,那么我们就要问了,handler与thread,looper等也是一一对应的吗? 这个就不是了,一个thread(looper)可以对应多个handler。具体为什么会是这样,我会在后面的代码分析中讲到。

 

二. 如何使用

    上面简单的讲解了他们之间的关系,下面我们简单讲解一下如何使用他们。我们知道在Android中使用他们是用来实现信息在不同的线程间传递的,而他们的操作是如何实现的那? 我们也用一个图来说明:

    从上图看要想实现在线程1中将信息传递给线程2,需要下面的几步:

1. 在线程2中创建一个handler,同时需要将这个handler与looper绑定(而每个looper都有对应的MessageQueue)

2. 在线程1中拥有线程而的handler(这个可以将线程2的handler作为线程1中的一个成员)

3. 在线程1中将获得的信息通过线程2的handler的sendMessage函数或者post等函数将message放入到handler对应的MessageQueue中

4. 在looper的loop函数中获得MessageQueue中的message,并将这个信息发送到handler的回调函数handleMessage中处理

 

三. 工作中如何使用

    下面我以我工作中用到的上面的信息传递方式来讲解。在我的工作中我们需要从一个信息处理线程中提取出部分需要特殊处理的事件,并对这部分事件做特殊的操作。具体方法为:

1. 我们新建一个线程来对特殊的事件进行处理,而这个线程会绑定一个自己的looper以及会有自己的handler

2. 在信息处理的线程中使用新建线程的handler将特定的信息使用sendMessage函数发出

3. 在新建线程的handler的回调函数handleMessage中处理这些信息

 

具体的代码实现:

1. 创建线程,获得looper,以及handler与looper绑定

Looper looper; // Create our own looper here 
mThread = new HandlerThread("mThread",Process.THREAD_PRIORITY_FOREGROUND); //建立handler的线程 
mThread.start(); //运行线程 
looper = mThread.getLooper(); //将当前的线程与looper绑定 
mHandler = new EventHandler(looper); //创建一个handler,并将其与looper绑定

    详细的代码我们后面讲。

2. 创建handler类

public class EventHandler extends Handler {
    public EventHandler(Looper looper) {
        super(looper);
    }
    @Override
    public void handleMessage(Message msg) {
        //处理信息
    }
}

3. 使用handler,从主线程获得信息并发送信息到处理特定信息的 线程

if (mHandler != null) {
    Message mmsg = Message.obtain(msg);  //拷贝Message
    mHandler.sendMessage(mmsg);
}

    通过上面的操作就可以简单的实现线程间传递信息了。

 

四. 参考文章:

Android 源码分析 —— Handler、Looper 和 MessageQueue : 讲解的非常好,以问答的方式将原理和代码结合,同时有Android中对于handler,looper以及MessageQueue的介绍

Android中Thread、Handler、Looper、MessageQueue的原理分析 :讲解很详细,上面handler,looper与MessageQueue的关系图就是来自这篇文章

 

五. 代码分析:

    代码分析(在这里方法和函数是一个意思,成员和变量是一个意思):

1. HandlerThread

public class HandlerThread extends Thread {
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    } 
    
    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;
    }
    
}

    我们代码中用到的方法:

mThread = new HandlerThread("mThread",Process.THREAD_PRIORITY_FOREGROUND);   //建立handler的线程
mThread.start();      //运行线程
looper = mThread.getLooper();   //将当前的线程与looper绑定

     HandlerThread继承自thread,所以他也是一个线程类,而new HandlerThread()其实就是创建了一个线程,而线程有不同的状态,如下图:

    而当执行了线程的start方法之后,如果获得了CPU资源就会执行线程的run方法了,看run中的代码为:

    public void run() {
        mTid = Process.myTid();  //获得该进程中当前线程的ID
        Looper.prepare();   //初始化当前线程作为一个looper
        synchronized (this) {
            mLooper = Looper.myLooper();  // 返回当前线程的looper对象
            notifyAll();
        }
        Process.setThreadPriority(mPriority);  //设置线程的调度属性
        onLooperPrepared();  //这是个回调函数,如果你有特殊的事情需要在loop方法调用前操作,可以在这里设置
        Looper.loop();  //调用looper的loop函数来发送分发信息
        mTid = -1;
    }

    其实在上面的代码中主要实现的是将thread与looper的绑定。

    而代码:

looper = mThread.getLooper();   //将当前的线程与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;
    }

    从上面的代码可以知道,其实就是返回当前线程的looper。这里首先判断当前的线程是否还活着,如果还在运行,同时还没有创建好looper的时候我们就需要他一直等,直到创建好,之后我们将创建好的looper返回。其中mLooper就是threadlocal的一个成员变量,而当looper初始化好后就会赋值给这个变量。

 

2.  Looper :

    我想单单通过我上面说的threadlocal中run做的事是实现thread与looper的绑定。大家可能不太相信,下面我们分析一下他主要的代码就知道了,主要的代码如下:

Looper.prepare();   //初始化当前线程作为一个looper
mLooper = Looper.myLooper();  // 返回当前线程的looper对象
Looper.loop();  //调用looper的loop函数来发送分发信息

    而looper相关的代码为:

public final class 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));
    }   
    
     public static Looper myLooper() {
        return sThreadLocal.get();
    }   
    
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }  
    
    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.recycle();
        }
    }    
        
}

    下面开始讲解我的代码,首先讲解Looper.prepare(),而Looper.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));
    }

    而通过上面的代码我们可以知道thread和looper只能是一一对应的关系,因为当你第二次通过prepare来绑定thread与looper时,sThreadLocal.get()可以得到当前进程的looper,从而会报错。而当第一次调用prepare的时候就可以调用sThreadLocal.set方法将当前线程与looper绑定。而sThreadLocal.set的实现为,获得当前的线程,同时获得线程的map,之后将获得的当前线程与New出来的 looper 放入到map的键值对中,这样就实现了对线程与looper的绑定。下面是sThreadLocal.set方法:

public void set(T value) { 
    Thread t = Thread.currentThread(); 
    ThreadLocalMap map = getMap(t); 
    if (map != null) 
        map.set(this, value); 
    else 
        createMap(t, value); 
}

     下面我们分析一下new Looper(quitAllowed)主要做了什么:

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

    从上面代码看主要做了两件事情,第一是创建一个MessageQueue对象,第二是获得当前的线程。从这里我们知道thread和looper是互相持有的,我们既可以通过looper获得当前的线程,同时也可以通过当前的线程获得looper。

    由于我们只能调用一次looper的prepare,所以我们也就只能new 一次MessageQueue了,所以这里的looper与MessageQueue也是一一对应的。

mLooper = Looper.myLooper();  // 返回当前线程的looper对象

    我们通过上面的代码知道通过sThreadLocal.set将looper与当前的线程放入了一个线程的map中,而想要获得当前进程的looper就可以通过sThreadLocal.get来获得,具体代码为:

    public static Looper myLooper() {
        return sThreadLocal.get();
    }

     我们知道了looper与线程绑定,那么looper的作用是什么那?他绑定线程的作用是什么那?

     其实looper的作用就是将handler通过sandMessage中发送的Message获得,之后发送给handler对应的handleMessage函数。而他之所以需要绑定一个线程是因为loop函数需要在一个循环中不断的运行,而如果没有一个线程的话,他会一直占用主线程,这是不可以的。而要更加详细的了解looper就要看loop函数了:

Looper.loop();  //调用looper的loop函数来发送分发信息
  public static void loop() {
        final Looper me = myLooper();  //获得looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;  //获得MessageQueue
        ..................................
        for (;;) {
            Message msg = queue.next(); // might block   //获得MessageQueue中的message
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
        ..................................
            msg.target.dispatchMessage(msg);       //将message发出
        ..................................
            msg.recycle();            //清除这个message
        }
    }

    通过上面的代码我们可以看出loop主要做的就是通过Message msg = queue.next();获得信息,同时通过msg.target.dispatchMessage(msg); 将信息发送出去。而信息是如何发送出去的那? 这就要看dispatchMessage函数了。

 

3. handler:

    首先我们回答一下上面的问题,如何发送信息:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    这里处理message有三种情况:

第一种是有msg.callback就调用他的message的callback函数

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

第二种是有handler自己的callback函数,就调用handler的callback的handleMessage函数:mCallback.handleMessage(msg)

final Callback mCallback;

第三就是调用handler自己的handleMessage函数。

handleMessage(msg);

    这里我们可能就要问了,为什么会有第一种情况? 这就要说明一下postXXX等函数与sendXXX等函数的区别了,我们知道handler中有sendMessage等的sendXXXX以及post等的postXXX,他们有什么区别那?

1. sendXXX用来传递的是Message对象,而postXX用来传递的是Runnable对象。

2. postXXX之后也会调用sendXXX,而不同的就是为其传入了message.callback

     代码为:

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

    从上面的代码中我们知道postXX与sendXXX最后是一样传递的message,而不同的是postXXX的message会走他自己的回调函数,也就是Runnable。

    而第二种处理message的情况是我们在定义一个handler的时候自己实现的,如果我们希望自己的message处理函数由自己编写可以使用这个方式。

    这第三种方式就是handler类默认的了。

    下面我们就要讲解重点了,也就是handler:

public class Handler {
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }    
 
    public Handler(Looper looper) {
        this(looper, null, false);
    }  
         
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }   
     
    public final Message obtainMessage()
    {
        return Message.obtain(this);
    } 
    
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }    
    
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }  
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }    
            
}

    handler最主要的工作就是获得message,以及发送message,他们对应的函数分别为:obtainMessage和sendMessage

    先说obtainMessage,其实handler的obtainMessage函数最主要的还是调用Message类的obtain函数。

    而sendMessage函数就是将Message加入到MessageQueue中,

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;  //获得MessageQueue
        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;  //这个handler
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis); //将message放入到MessageQueue中
    }   

  上面我们说过一个looper可以对应多个handler,那么looper是如何判断MessageQueue中的message发送给那个handler的信息处理函数那? 这里就要看上面的enqueueMessage函数中的 msg.target = this; (这个this就是对应的handler)也就是说在将message放入到MessageQueue的时候就注定了这个message会发送给那个handler。而在looper的loop函数中就是调用msg.target.dispatchMessage(msg);  来分发message,也就是handler调用自己的dispatchMessage函数来分发message。

  public static void loop() {
        final Looper me = myLooper();  //获得looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;  //获得MessageQueue
        ..................................
        for (;;) {
            Message msg = queue.next(); // might block   //获得MessageQueue中的message
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
        ..................................
            msg.target.dispatchMessage(msg);       //将message发出
        ..................................
            msg.recycle();            //清除这个message
        }
    }

    好了,讲到这里就对上面的代码进行了一些讲解,希望这部分可以加深你对handler,looper以及message的了解。

 

六. 参考文章 : 

啃碎并发(二):Java线程的生命周期

Java多线程学习(三)---线程的生命周期

Java 之 ThreadLocal 详解

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章