Android异步消息处理机制之looper机制

本文详细介绍了Android系统中Looper和Handler的工作原理。Looper负责管理消息队列,而Handler用于发送和处理消息。文章通过示例代码展示了如何创建Looper和Handler,并解释了消息循环的过程。

   在android系统的应用程序中,与java程序相似,线程之间都是靠消息来驱动的,与此工作相关的是由handler,looper以及message,messagequene来完成。简单来说就是每一个线程中有且只有一个消息队列MessageQueue,利用looper来获取当前的线程和消息队列,我们可以不断向这个消息队列中添加消息Message,接着再利用handler从looper得知当前的消息队列,后再从中取出消息,处理消息。本文中代码示例参考了http://blog.csdn.net/smbroe/article/details/44239961中的代码并对其稍作改动。

  什么是looper?looper里主要有两部分,一部分是线程,一部分是消息队列,一个looper就对应了一个当前的线程和当前线程下的消息队列。可以将looper比喻为工厂生产时用到的机械臂,不停地将从生产带上获取最新的产品(在消息列表中读取最新的消息),同时间最新的产品不停地打包(即将最新的消息不停地分发给handler),起着枢纽的作用。

 我们通过一个简单的例子来学习如何使用Looper和Handler(handler的工作原理在下一篇博客中会讲,在这里只需大概理解为handler是发送消息队列中的消息并处理消息的就行了,本文只介绍下Looper),并通过这个例子来研究一下其工作原理; 

  首先,我们在LooperThread中为消息循环做准备,并创建一个Handler用于处理消息,注意Handler的创建要在调用Looper.prepare()之后,具体原因在等会会说明:

 

public class LooperThread extends Thread {

    public Handler mHandler;

    @Override
    public void run() {
        // TODO Auto-generated method stub
        Looper.prepare();
        synchronized (this) {
            mHandler = new Handler() {

                @Override
                public void handleMessage(Message msg) {
                    // TODO Auto-generated method stub
                    super.handleMessage(msg);
                    Log.w("LooperThread", "handleMessage::Thread id---" + getId());
                }

            };
        }
        Looper.loop();
        notifyAll();
    }

}
     然后我们在主线程中创建一个新的线程,并通过新线程中的Handler向LooperThread线程发送消息;

 

 final LooperThread mLooperThread = new LooperThread();
    mLooperThread.start();
    new Thread() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (mLooperThread.mHandler == null) {
                try {
                    wait();//防止在发送消息时Handler还没建立
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            mLooperThread.mHandler.sendEmptyMessage(0);
            Log.w(TAG, "Send Message::Thread id ---" + getId());
        }
    }.start();

        程序运行的结果如下:


        可以看到线程之间通信成功了,下面我们再来审视整个过程,通过查看关键函数的源代码来去理解整个运作机制。

       首先在LooperThreda中调用Looper.prepare()方法,我们查看其源代码:

       

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();
    }
        其中sThreadLocal就是looper里内置的数据管理类,里面存储着looper,定义如下
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

       学过数据结构的同学应该很容易能根据其get(),set()方法想起链表,这里我就不对其多做分析了,总之就是用来存looper的数据结构罢了。接着是looper的构造器,从代码中我们可以看到主要就是新建一个消息队列并存于内部数据mQuene和获得当前的线程存于mThread,在这里消息队列和线程是相互绑定的,因此也验证了我们开头的结论,一个looper就对应了一个当前的线程和当前线程下的消息队列。回到示例代码中,接着就是新建了一个handler(代码中的扩展:synchronizedjava里同步机制的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码,可以理解为两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码)
,并重写了handler的
handleMessage方法用来对消息进行处理,这里我们先手动忽略这个方法一会再来分析。

  接着调用Looper的loop方法。该方法实现了消息循环,源码如下:

  public static void loop() {
        //myLooper()方法就是通过sThreadLocal.get()返回我们刚刚设置的Looper
        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;

        // 确保线程和本地进程是一致的,并且记录这个identity token
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // 可能会阻塞
            if (msg == null) {
                // 没有消息则表明这个消息队列退出了.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
//Printer表示打印事件
            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);
            }

            // 确保在分发的过程中该线程没有崩溃
            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();
        }
    }
   这段代码中涉及到的知识比较多,我们只对与本文有关的关键方法进行讲解,其中通过mylooper()方法来获得刚在prepaer()方法中新建的looper,然后获得当前的消息队列,接着用一个for( ; ; )空循环来不断从消息队列中读取消息存于msg(msg=quene.next()),最后调用msg.target.dispatchMessage(msg)来讲消息分发出去,分发到哪里?分发到handler里交给它处理,有兴趣的同学可以先看看其源代码(具体分析下一篇才会讲):

   

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

   可以看到最后又是通过调用我们重写的handleMessage来进行处理的。

   接着是调用 notifyAll()来告诉等着调用被之前synchronized锁起来的代码的线程们,告诉它们我用完了,你们可以去抢着用了。

   最后来看主线程中的示例代码中的 mLooperThread.mHandler.sendEmptyMessage(0);当我们调用了sendMessage方法之后就向Looper发送了一条消息,让我们看看这个方法,

消息是如何被传递的。sendMessage方法最终会调用到sendMessageAtTime方法来,来看看源代码:

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

可以看到,sendMessage方法最终调用了queue.enqueueMessage方法将消息加入到了Looper中的消息队列。那么整个线索就联系起来了,给handler

发消息,改消息到了looper的消息队列里,然后looper把这个消息取出来在交给looper来处理。这就是示例代码的整个运作过程,因此来总结一下吧。

  1.首先是调用Looper.prepare()方法来创建一个新的队列和获取到当前的线程,

  2.然后我们创建handler并重写其handleMessage方法,在我们构建handler的时候会获取到当前的looper对象(通过该looper对象来获取消息队列),消息队列和Callback对象(当然,在上述代码中Callback对象为null),紧接着我们在主线程里开的新线程中调用sendMessage方法来向handler中的消息队列来传递消息(当然,查看源代码可以知道其最终调用的是sendMessageAtTime方法,并通过返回enqueueMessage方法来讲消息加入到消息队列中),我们重写的handleMessage方法就是用来拦截我们感兴趣的消息并对消息进行处理的方法

  3.最后是调用Looper.loop()来实现消息的循环分发,其通过queue.next()来不断获取消息队列中的最新消息并且调用 msg.target.dispatchMessage(msg)方法来实现消息的分发

  4.最后是dispatchMessage方法实现消息的分发,在该方法中进行了一系列判断:

  如果Message自带了Callback,则交给Message的Callback处理;

  如果Handler了设置了Callback,则交给Handler的Callback处理;

  如果两者都没有,则调用handleMessage方法处理(默认是不处理的)。

也可以参考下以下图片:






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值