天天看点

Android JNI知识简介

转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/17361775

上周对android中的事件派发机制进行了分析,这次博主要对消息队列和looper的源码进行简单的分析。大家耐心看下去,其实消息队列的逻辑比事件派发机制简单多了,所以大家肯定会很容易看懂的。

消息队列在android中对应messagequeue这个类,顾名思义,消息队列中存放了大量的消息(message)

消息(message)代表一个行为(what)或者一串动作(runnable),有两处会用到message:handler和messenger

handler大家都知道,主要用来在线程中发消息通知ui线程更新ui。messenger可以翻译为信使,可以实现进程间通信(ipc),messenger采用一个单线程来处理所有的消息,而且进程间的通信都是通过发消息来完成的,感觉不能像aidl那样直接调用对方的接口方法(具体有待考证),这是其和aidl的主要区别,也就是说messenger无法处理多线程,所有的调用都是在一个线程中串行执行的。messenger的典型代码是这样的:new messenger(service).send(msg),它的本质还是调用了handler的sendmessage方法

looper是循环的意思,它负责从消息队列中循环的取出消息然后把消息交给目标处理

线程如果没有looper,就没有消息队列,就无法处理消息,线程内部就无法使用handler。这就是为什么在子线程内部创建handler会报错:"can't create handler inside thread that has not called looper.prepare()",具体原因下面再分析。

在线程的run方法中加入如下两句:

looper.prepare();

looper.loop();

这一切不用我们来做,有现成的,handlerthread就是带有looper的线程。

想用线程的looper来创建handler,很简单,handler handler = new handler(thread.getlooper()),有了上面这几步,你就可以在子线程中创建handler了,好吧,其实android早就为我们想到这一点了,也不用自己写,intentservice把我们该做的都做了,我们只要用就好了,具体怎么用后面再说。

一个handler会有一个looper,一个looper会有一个消息队列,looper的作用就是循环的遍历消息队列,如果有新消息,就把新消息交给它的目标处理。每当我们用handler来发送消息,消息就会被放入消息队列中,然后looper就会取出消息发送给它的目标target。一般情况,一个消息的target是发送这个消息的handler,这么一来,looper就会把消息交给handler处理,这个时候handler的dispatchmessage方法就会被调用,一般情况最终会调用handler的handlemessage来处理消息,用handlemessage来处理消息是我们常用的方式。

源码分析

[java] view

plaincopy

Android JNI知识简介
Android JNI知识简介

   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;  

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

       //这里msg被加入消息队列queue  

       return queue.enqueuemessage(msg, uptimemillis);  

Android JNI知识简介
Android JNI知识简介

public static void loop() {  

     final looper me = mylooper();  

     if (me == null) {  

         throw new runtimeexception("no looper; looper.prepare() wasn't called on this thread.");  

     }  

     //从looper中取出消息队列  

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

         //将消息交给target处理,这个target就是handler类型  

         msg.target.dispatchmessage(msg);  

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

 }  

Android JNI知识简介
Android JNI知识简介

/** 

 * subclasses must implement this to receive messages. 

 */  

public void handlemessage(message msg) {  

 * handle system messages here. 

public void dispatchmessage(message msg) {  

    if (msg.callback != null) {  

        //这个方法很简单,直接调用msg.callback.run();  

        handlecallback(msg);  

    } else {  

        //如果我们设置了callback会由callback来处理消息  

        if (mcallback != null) {  

            if (mcallback.handlemessage(msg)) {  

                return;  

            }  

        }  

        //否则消息就由这里来处理,这是我们最常用的处理方式  

        handlemessage(msg);  

我们再看看msg.callback和mcallback是啥东西

/*package*/ runnable callback;   

现在已经很明确了,msg.callback是个runnable,什么时候会设置这个callback:handler.post(runnable),相信大家都常用这个方法吧

Android JNI知识简介
Android JNI知识简介

 /** 

 * callback interface you can use when instantiating a handler to avoid 

 * having to implement your own subclass of handler. 

 * 

 * @param msg a {@link android.os.message message} object 

 * @return true if no further handling is desired 

public interface callback {  

    public boolean handlemessage(message msg);  

final callback mcallback;  

而mcallback是个接口,可以这样来设置 handler handler = new handler(callback),这个callback的意义是什么呢,代码里面的注释已经说了,可以让你不用创建handler的子类但是还能照样处理消息,恐怕大家常用的方式都是新new一个handler然后override其handlemessage方法来处理消息吧,从现在开始,我们知道,不创建handler的子类也可以处理消息。多说一句,为什么创建handler的子类不好?这是因为,类也是占空间的,一个应用class太多,其占用空间会变大,也就是应用会更耗内存。

Android JNI知识简介
Android JNI知识简介

@override  

public void run() {  

    mtid = process.mytid();  

    looper.prepare();  

    synchronized (this) {  

        mlooper = looper.mylooper();  

        notifyall();  

    process.setthreadpriority(mpriority);  

    onlooperprepared();  

    looper.loop();  

    mtid = -1;  

handlerthread继承自thread,其在run方法内部为自己创建了一个looper,使用上handlerthread和普通的thread不一样,无法执行常见的后台操作,只能用来处理新消息,这是因为looper.loop()是死循环,你的code根本执行不了,不过貌似你可以把你的code放在super.run()之前执行,但是这好像不是主流玩法,所以不建议这么做。

Android JNI知识简介
Android JNI知识简介

public void oncreate() {  

    // todo: it would be nice to have an option to hold a partial wakelock  

    // during processing, and to have a static startservice(context, intent)  

    // method that would launch the service & hand off a wakelock.  

    super.oncreate();  

    handlerthread thread = new handlerthread("intentservice[" + mname + "]");  

    thread.start();  

    mservicelooper = thread.getlooper();  

    mservicehandler = new servicehandler(mservicelooper);  

intentservice继承自service,它是一个抽象类,其被创建的时候就new了一个handlerthread和servicehandler,有了它,就可以利用intentservice做一些优先级较高的task,intentservice不会被系统轻易杀掉。使用intentservice也是很简单,首先startservice(intent),然后intentservice会把你的intent封装成message然后通过servicehandler进行发送,接着servicehandler会调用onhandleintent(intent

intent)来处理这个message,onhandleintent(intent intent)中的intent就是你startservice(intent)中的intent,ok,现在你需要做的是从intentservice派生一个子类并重写onhandleintent方法,然后你只要针对不同的intent做不同的事情即可,事情完成后intentservice会自动停止。所以,intentservice是除了thread和asynctask外又一执行耗时操作的方式,而且其不容易被系统干掉,建议关键操作采用intentservice。

Android JNI知识简介
Android JNI知识简介

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

    //获取当前线程的looper  

    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;  

    mcallback = callback;  

    masynchronous = async;  

如何避免这种错误:在ui线程使用handler或者给子线程加上looper。

继续阅读