Android Handler、Looper、Message Queue理解


概要
Swingと同様に、AndroidのUIスレッドはスレッドセキュリティではなく、単一スレッドモデルを採用しています.しかし、Androidは強力なメッセージングメカニズムメッセージキュー(Message Queue)とHandlerを提供しています.メッセージキューを使用するスレッドを(Message Loop.)と呼びます.Looperというオブジェクトは、メッセージキューに新しいメッセージがあるかどうかを繰り返しチェックします.メッセージループは、スレッドとLooperで構成されます.Looperオブジェクトはスレッドのメッセージキューを管理しています.
プライマリ・スレッドもメッセージ・ループであるため、Looperがあり、
プライマリ・スレッドのすべての作業は、そのLooperによって行われ、looperはメッセージ・キューからメッセージをキャプチャし続け、メッセージによって指定されたタスクを完了します.
注意:ActivityのonCreate、onDestroy()など、プライマリ・スレッドのすべての作業がそのLooperによって完了していることについての理解.後で分析します.
Handler、Looper、MessageQueueの動作原理
Message:メッセージ・オブジェクト.3つの属性があります
  • what:int型メッセージコード;
  • obj:メッセージとともに送信オブジェクト
  • target:メッセージを処理するHandler
  • Looper:スレッドごとに1つのLooperしか持てません.そのloopメソッドは,メッセージキューMessageQueueからメッセージを抽出し,メッセージを送信するHandlerにメッセージを渡して処理する.メインスレッドのLooperはシステムが初期化を担当します.自分で起動したスレッドはLooperを呼び出す必要がある.prepare()を初期化し、Looperを呼び出す.loop()はメッセージループを開始します.
    MessageQueue:FIFOメッセージキュー.Messageの管理に使用します.Looperは初期化に対応するMessageQueueを作成します.
    Handler:メッセージの送信とメッセージの受信の2つの役割.
    正常に動作するには、現在のスレッドに動作するLooperオブジェクトがあることを確認する必要があります.さもないと間違いを報告します.デフォルトではHandlerは作成したスレッドで動作します.
    非UIスレッドでHandlerを使用する方法:
    方法1:UIスレッドでHandlerを初期化し,パラメータを介して他のスレッドのコンストラクタに伝達する.このようにして作成するHandlerは、メインスレッドで動作Handlerである.
    方式2:新規スレッドでLooperを初期化し,そのprepare()を呼び出すことによりloop()メソッドでLooperとメッセージキューを初期化し,Handlerの使用を初期化する.
    方式の3:APIの中のを使います
    HandlerThread.これはLooperを含み、自動的にLooperを初期化するThreadです.注意HandlerThreadの
    onLooperPrepared()メソッドは、Handlerを初期化するのがベストです.
    なぜ?loop()ループはプライマリスレッドをブロックしませんか?
    プライマリ・スレッドでも他のスレッドでもLooperを使用するには、次の手順に従います.
    Looper.prepare();
    Looper.loop();//これはワイヤレスループです.プライマリ・スレッドLooperがプライマリ・スレッド上で実行されていることを知っています.なぜプライマリ・スレッドがブロックされないのでしょうか.
    Looper.quite();
    まずLooperを見てみましょうJAvaの一部のソースコード:
    public static void loop() {
            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;
    
            // 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);
                }
    
                msg.target.dispatchMessage(msg);
    
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
    
    明らかfor(;;)これはワイヤレスサイクルです.では、なぜプライマリ・スレッドで実行してもブロックされないのでしょうか.
    次に、ActivityのonCreateメソッドにint a=1/0を追加します.
     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            int a=1/0;
    を実行し、exception stack traceを分析して答えを見つけることができます.以下はstack traceです.
    FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity 
    java.lang.ArithmeticException: divide by zero
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
    at android.app.ActivityThread.access$600(ActivityThread.java:130)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(Activity Thread.java:4745)見たでしょ?元のメインスレッドで実行する過程でActivityのonCreateメソッド呼び出しもLooperのloopメソッドで実行する.他の方法のテストはstack traceによって答えを見つけるのと似ています.
    したがって、Activityのライフサイクルなど、UIスレッドで発生するすべてのことはloopサイクルによって直接的または間接的に処理する必要があることがわかる.loopは無限ループですが、UIスレッドはブロックされません.
    Looperを通してmyLooper==Looper.getMainLooper()はUIスレッドにあるかどうかを判断する