天天看點

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。

繼續閱讀