androidスレッド間メッセージ処理メカニズム(Looper、Handler、Message)の概要


詳細
 
役割:Androidのスレッド間メッセージ処理メカニズムは、主にメインスレッド(UIスレッド)とワークスレッド(自分で作成したスレッド)との通信を処理するために使用されます.例えば、ワークスレッドによってインタフェースをリフレッシュしたり、ワークスレッドにdialogやToastを作成したりします.
ワークスレッド:androidアプリケーションで作成したActivity、Service、Broadcastなどはメインスレッド(UIスレッド)で処理されていますが、I/O読み書きの大きなファイル読み書き、データベース操作、ネットワークダウンロードなど、比較的時間のかかる操作があります.ユーザーインタフェースをブロックしないようにANRの応答プロンプトウィンドウが表示されます.このとき,Threadクラスを継承したりRunnableインタフェースを実装したりする作業スレッドの作成を検討して解決することができる.
ワークスレッドを使用すると発生しやすい問題:AndroidプラットフォームではUIコントロールがスレッドセキュリティタイプに設計されていないため、リフレッシュするための同期メカニズムを導入する必要があります.そうしないと、ワークスレッドを使用してUIを更新すると異常になります.
 
Looper:
以上の問題に対して、androidはメッセージループ機構を採用してスレッド間の通信を処理し、Androidメッセージループはスレッドに対して(各スレッドには独自のメッセージキューとメッセージループがあることができる)、AndroidシステムではLooperがスレッドのメッセージキューとメッセージループを管理し、Loopを通過することができる.myLooper()は、現在のスレッドのLooperオブジェクトを得る、Loop.getMainLooper()は、現在のプロセスのメインスレッドのLooperオブジェクトを取得できます.Looperオブジェクトは何ですか?実はAndroidの各Threadは1つのLooperに対応しており、LooperはThreadがマルチスレッド間でメッセージを伝えるループを担当するメッセージキューを維持するのに役立ちます.1つのスレッドは存在する(もちろん存在しなくてもよい)1つのメッセージキューと1つのメッセージループ(Looper)であるが、作成されたワークスレッドのデフォルトはメッセージループとメッセージキューがない.このスレッドにメッセージキューとメッセージループを持たせるには、スレッドでまずLooperを呼び出す必要がある.prepare()はメッセージキューを作成し、Looperを呼び出します.loop()はメッセージループに入ります.次の例を示します:class LooperThread extends Thread{
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler =newHandler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}

 
 
Looper.prepare():Looperオブジェクトの作成はprepare関数によって行われ、各Looperオブジェクトはスレッドに関連付けられます.具体的な操作はソースコードを参照してください.
 
 
public static final void prepare() {     
     if (sThreadLocal.get() != null) {     
         throw new RuntimeException("Only one Looper may be created per thread");     
     }     
     sThreadLocal.set(new Looper());     
}   

 
 
Looperオブジェクトが作成されるとMessageQueueが作成され、メインスレッドにはMessageQueueがデフォルトで作成され、その他のスレッドにはMessageQueueがデフォルトではなくMessage(メッセージ)が受信できません.Messageを受信する必要がある場合はprepare関数でMessageQueueを作成する必要があります.具体的な操作はソースコードを参照してください.
 
 
private Looper() {     
      mQueue = new MessageQueue();     
      mRun = true;     
     mThread = Thread.currentThread();     
  }     

 
 
Looper.loop():Loop関数はMessage Queueから前後にMessageを取り出し、HandlerのdispatchMessage関数でメッセージの処理を行い(メッセージの処理はHandlerが担当していることがわかる)、メッセージの処理が完了したらMessageオブジェクトのrecycle関数でMessage Poolに入れて次回使用するため、Poolの処理により、一定のメモリ管理が提供され、メッセージ・オブジェクトの取得が加速します.タイミング処理が必要なメッセージをどのようにタイミング処理するかについては、Message Queueのnext関数を参照してください.Messageを取って処理を行う際に、Message Queueの中のMessageが時間の要求に合っているかどうかを判断することによって、Messageを取り出して処理する必要があるかどうかを決定します.このようにしてメッセージのタイミング処理を行います.具体的な操作はソースコードを参照してください.
 
 
public static final void loop() {     
    Looper me = myLooper();     
     MessageQueue queue = me.mQueue;     
     while (true) {     
        Message msg = queue.next(); // might block     
     //if (!me.mRun) {     
         //    break;     
          //}     
            if (msg != null) {     
               if (msg.target == null) {     
                   // No target is a magic identifier for the quit message     
                  return;     
               }     
               if (me.mLogging!= null)      
                   me.mLogging.println(">>>>> Dispatching to " + msg.target + " "+ msg.callback + ": " + msg.what);     
               msg.target.dispatchMessage(msg);     
              if (me.mLogging!= null)      
                   me.mLogging.println("<<<<< Finished to" + msg.target + " "+ msg.callback);     
              msg.recycle();     
          }     
      }     
  }  

 
next()関数を見て
 
    final Message next() {
        boolean tryIdle = true;

        while (true) {
            long now;
            Object[] idlers = null;
    
            // Try to retrieve the next message, returning if found.
            synchronized (this) {
                // is counted in milliseconds since the system was booted,not counting time spent in deep sleep.
                now = SystemClock.uptimeMillis();
                
                Message msg = pullNextLocked(now);
                if (msg != null) return msg;
                if (tryIdle && mIdleHandlers.size() > 0) {
                    idlers = mIdleHandlers.toArray();
                }
            }
    
            // There was no message so we are going to wait...  but first,
            // if there are any idle handlers let them know.
            boolean didIdle = false;
            if (idlers != null) {
                for (Object idler : idlers) {
                    boolean keep = false;
                    try {
                        didIdle = true;
                        keep = ((IdleHandler)idler).queueIdle();
                    } catch (Throwable t) {
                        Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                    }

                    if (!keep) {
                        synchronized (this) {
                            mIdleHandlers.remove(idler);
                        }
                    }
                }
            }
            
            // While calling an idle handler, a new message could have been
            // delivered...  so go back and look again for a pending message.
            if (didIdle) {
                tryIdle = false;
                continue;
            }

            synchronized (this) {
                // No messages, nobody to tell about it...  time to wait!
                try {
                    if (mMessages != null) {
                        if (mMessages.when-now > 0) {
                            Binder.flushPendingCommands();
                            this.wait(mMessages.when-now);
                        }
                    } else {
                        Binder.flushPendingCommands();
                        this.wait();
                    }
                }
                catch (InterruptedException e) {
                }
            }
        }
    }
 
 
 
final Message pullNextLocked(long now) {
        Message msg = mMessages;
        if (msg != null) {
            if (now >= msg.when) {
                mMessages = msg.next;
                if (Config.LOGV) Log.v(
                    "MessageQueue", "Returning message: " + msg);
                return msg;
            }
        }

        return null;
    }

 
 
Handler:
これにより、スレッドにはメッセージ処理メカニズムがあり、Handlerでメッセージ処理を行います.ActivityはUIスレッド(プライマリスレッド)であり、Androidシステムは起動時にActivityのためにメッセージキューとメッセージループ(Looper)を作成します.Handlerの役割は、メッセージを特定のメッセージキューに追加し、そのメッセージキュー内のメッセージを配布および処理することである.Handlerを作成するときにLooperオブジェクトを指定し、指定しない場合は現在のスレッドのLooperを使用して作成します.1つのActivityでは、複数の作業スレッドまたは他のコンポーネントを作成できます.これらのスレッドまたはコンポーネントがActivityのプライマリ・スレッド・メッセージ・キューにメッセージを入れると、メッセージはプライマリ・スレッドで処理されます.メインスレッドは一般にインタフェースの更新操作を担当するため,この方式はAndroidインタフェースの更新をうまく実現できる.では、別のスレッドはどのようにしてメインスレッドのメッセージキューにメッセージを入れますか?Handleオブジェクトによって、HandlerオブジェクトがプライマリスレッドのLooperで作成されると、HandlerのsendMessageが呼び出され、プライマリスレッドのメッセージキューにメッセージが格納されます.プライマリ・スレッドでhandlerのhandleMessageメソッドが呼び出され、メッセージが処理されます.
 
Message:
メッセージの取得:MessageのobtainメソッドによってMessageオブジェクトまたはMessageオブジェクトを直接newする.ソースコードは以下の通りです.
public final Message obtainMessage(int what, int arg1, int arg2, Object obj){     
     return Message.obtain(this, what, arg1, arg2, obj);     
 }    
 
Message.Obtain関数:Message PoolからMessageを取り出し、Message PoolにMessageがない場合は新しいMessageを返します.
Message Pool:サイズは10個です.
MessageのクリーンアップはLooperのloop関数で、処理したMessageをMessageのPoolに入れ、最大値10個を超えた場合、このMessageオブジェクトを破棄します.
メッセージの送信:MessageQueueのenqueueMessageによってMessageオブジェクトをMessageQueueの受信メッセージキューに配置します.ソースコードは次のとおりです.
 
public boolean sendMessageAtTime(Message msg, long uptimeMillis){     
       boolean sent = false;     
       MessageQueue queue = mQueue;     
       if (queue != null) {     
           msg.target = this;     
        sent = queue.enqueueMessage(msg, uptimeMillis);     
        } else {     
            RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");     
            Log.w("Looper", e.getMessage(), e);     
       }     
       return sent;     
 }     

 
 
スレッドはどのようにMessageQueueの中で受信するメッセージを処理します:Looperのloop関数の中で循環してMessageQueueの中のメッセージを取り出して、それからHanderのdispatchMessage関数を呼び出してメッセージに対して処理して、ソースコードは以下の通りです:
 
 
public void dispatchMessage(Message msg) {     
      if (msg.callback != null) {     
          handleCallback(msg);     
    } else {     
        if (mCallback != null) {     
             if (mCallback.handleMessage(msg)) {     
                 return;     
           }     
         }     
         handleMessage(msg);     
     }     
 }     

 
 
参照先:
http://www.android123.com.cn/androidkaifa/422.html
http://dev.10086.cn/cmdn/wiki/index.php?edition-view-2600-1.html
http://hi.baidu.com/dragon_eros/blog/item/6eaf600cb4e22f28e824881c.html