Android下的多线程通信机制

来源:互联网 发布:开源淘宝客 导入 编辑:程序博客网 时间:2024/04/27 20:37

Android下的多线程通信机制

这几天一直在做和android 多线程之间通信相关的工作,UI线程和子线程之间进行通信以及使用子线程进行UI界面更新,也阅读了Looper、Handler、Message这几个类的源码,同时也参考网上一些博客,有一些使用心得,特此记录下来。

其实,在我的android程序中进行通信的只有三个线程,暂且叫UI线程,work线程和Log捕获线程吧。Log捕获线程用于抓取Activity Manager的Log信息并发送信息到UI线程,work线程接收UI线程发送的消息并执行具体的工作。

android多线程之间的通信是通过线程之间的消息机制驱动的,android的消息处理机制有四大核心类:Looper, Handler, Message, Message Queue。当然我们在使用时几乎不会和Message Queue直接打交道,但它也是组成android消息处理机制不可或缺的一部分。

下面通过源码来看android消息处理机制的实现过程。

Looper的奇妙

从面上看Looper循环者的意思,Looper被设计把一个普通的线程变成一个Looper线程即循环工作的线程。通常我们需要循环工作的线程,不断的等待新任务的出现并执行。创建一个Looper线程很简单,代码如下:

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

        //... 其他work

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

 

通过上面两行核心代码,我们的线程就变成了Looper线程,如此神奇,下面我们看看Looper源码是如何实现的。

 1) Looper.prepare();

public final class Looper {

// 每个线程中的Looper对象都是一个ThreadLocal,线程的本地存储变量.

//这是创建Looper对象的核心,下面获取的是每个线程的Looper本地变量
    static final ThreadLocal<Looper> sThreadLocal new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class
    final MessageQueue mQueue; //消息队列
    final Thread mThread;  //当前线程
     /** 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));
    }

/**
 * Return the {
@link MessageQueue} object associated with the current
 * thread.  This must be called from a thread running a Looper, or a
 * NullPointerException will be thrown.
 */

private Looper(boolean quitAllowed) {
    mQueue new MessageQueue(quitAllowed);  //Looper线程的消息队列
    mThread = Thread.currentThread();  //Looper所属的线程
}

 

通过上面的源码,Looper.prepara();在线程的本地存储变量中创建Looper对象,并且只创建一个Looper对象。

2) Looper.loop();

当线程调用该方法之后,Looper线程开始真正的工作,不断的从自己的Message Queue中取出消息执行,源码如下:

/**
 * 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(); //获取当前线程的Looper对象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue; //获取当前Looper的Message Queue

    //进入循环,取出消息 执行消息
    for (;;) {
        Message msg = queue.next()// 取出message
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }


        ........
        //关键点所在
        msg.target.dispatchMessage(msg);  //分派给Message实例关联的target去执行

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

 

通过以上我们可以得出一下结论:

1. 每个线程只能有一个Looper实例, 因为它是一个ThreadLocal变量。

2. 通过调用Looper.prepare()方法,使一个普通线程摇身一变成为Looper线程

3. Looper内部有一个MessageQueue实例, 通过调用Looper.loop()方法,线程开始不断的从消息队列取出消息并执行。

 

那么, Looper和MessageQueue和当前的线程都已经关联起来了,是怎么发送消息到到MessageQueue的,消息的发送和处理是怎样的,这就是Handler的工作。

Handler、Message、Handler之间的关系为:

Looper中有一个消息队列Message,里面存储的一个个待处理的消息

Message中有一个Handler,用来处理Message

Handler中的Message指向Looper中的Message

Handler消息的异步处理者

在整个的android消息处理机制中,handler是很重要的,handler负责发送消息和处理自己发送的消息,即是handler将消息送到消息队列(MessageQueue), 当前线程的Handler实例发送消息到自己的关联的Looper线程,然后Handler在自己关联的线程中去处理其他线程发送的消息,整个过程是异步实现的。

首先,我们得知道,即使没有Handler,我们仍然可以实现将消息发送到Message队列

因为,Looper中包含Message实例,可以实例化一个Message去发送消息,但是会比较麻烦,Handler出现,封装消息,将消息插入到消息队列都变的简单多了。

下面看Handler的构造函数:

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();  //关联当前线程的Looper

    //Looper不能为空
    if (mLooper == null) {  
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue mLooper.mQueue; //关联Looper的MessageQueue.
    mCallback = callback;
    mAsynchronous = async;
}

一个线程可以有多个Handler,但是一个线程只能有一个Looper

HandlerThread Looper线程

HandlerThread 解决了HandlerLooper之间的同步问题

HandlerThread继承Thread,当我们创建一个HandlerThread实例时,就创建了一个Looper线程

源码如下:

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

 

run方法中通过Looper.prepare() Looper.loop() 两个方法将此线程转化为Looper线程,这样该线程就拥有自己的消息队列,通过Handler就可以和其他线程进行异步通信。

HandlerThread也提供了获取关联当前线程的方法:

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

 

为什么会存在HandlerLooper之间的线程同步问题呢?

因为Android Handler机制本来就是用于解决线程之间通信的问题。例如:

public LooperThread extends Thread {

public Looper myLooper = null;

//假设LooperThread为线程2

public void run() {

Looper.prepare();

myLooper = Looper.myLooper();

Looper.loop();

}

}

 

在线程中获取线程myLooper;

{

LooperThread lpThread = new LooperThread();

lpThread.start();  //启动线程2

Looper looper = lpThread.myLooper(); //get the Looper of thread 2

Handler handler = new Handler(looper);

}

很明显,红色标志的代码是有问题的,因为looper可能为空。为什么?

原因在于在线程在获取线程2myLooper,首先的得保证线程2myLooper不为空。

所以,HandlerThread的上诉代码就好理解了,如果新线程还没有创建Looepr对象,则等待

然后notifyAll,表明已经获取到新线程的Looper对象。

使用HandlerThread的方法:

 

HandlerThread handlerThread = new HanlerThread(“Handler”);

handlerThread.start(); 

以上两行就创建并启动了一个HandlerTHread

但是,想让此线程循环的工作,还应该绑定Handler实例。

Handler handler = new Handler(handlerThread.getLooper(), handlerThread );

Handler的构造函数较多,根据自己的需要选择构造Handler实例

0 0
原创粉丝点击