AndroidスレッドのLooperに関する知識

7590 ワード

AndroidスレッドのLooper,Handlerに関する知識
AndroidのLooperクラスは、androidスレッドでメッセージ処理を行うためのメッセージループとメッセージキューをカプセル化するためのクラスです.Handlerは、メッセージキューにメッセージを挿入するためのツールクラスと見なすことができる.
Android     Looper   : Class used to run a message loop for a thread. Threads by
default do not have a message loop associated with them; to create one, call prepare() in
the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler class. 
This is a typical example of the implementation of a Looper thread, using the separation 
of prepare() and loop() to create an initial Handler to communicate with the Looper.

Looper実現原理
1. Looper            
2. Looper      ,       MessageQueue(    )。
3.                 。
4.    Looper     ,     “  ”    ,      ,    ,       
。         ,        ,  handler handlemessage      

Looper作成プロセス
  • Looperクラスは、スレッドのメッセージループを開くために使用されます.デフォルトではandroidで新しく誕生したスレッドはメッセージループを開いていません.(メインスレッドを除くと、メインスレッドシステムは自動的にLooperオブジェクトを作成し、メッセージループを開きます.)LooperオブジェクトはMessageQueueによってメッセージとイベントを格納します.1つのスレッドには1つのLooperしかありません.MessageQueueに対応します.
  • は通常、HandlerオブジェクトによってLooperと対話する.Handlerは、指定されたLooperにメッセージを送信し、処理方法を定義するためのLooperのインタフェースと見なすことができる.デフォルトでは、Handlerは、定義されたスレッドのLooperにバインドされます.たとえば、Handlerはプライマリ・スレッドで定義され、プライマリ・スレッドのLooperにバインドされます.mainHandler = new Handler()new Handler(Looper.myLooper())と等価である.Looper.myLooper():現在のプロセスのlooperオブジェクトを取得し、同様のLooper.getMainLooper()を使用してメインスレッドのLooperオブジェクトを取得します.
  • は、非プライマリ・スレッドで直接new Handler()が次のエラーを報告します.
    E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught 
    exception E/AndroidRuntime( 6173): Java.lang.RuntimeException: Can't create handler inside 
    thread that has not called Looper.prepare()
    
    は、非プライマリ・スレッドではデフォルトでLooperオブジェクトが作成されていないため、Looper.prepare()を呼び出してLooperを有効にする必要があります.Looper.prepare()関連コード:
    /**
      *    Looper,  loop()      ,  quit()  
      * Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }
    
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//         Looper
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //  Looper
        sThreadLocal.set(new Looper(quitAllowed));
    }
    
    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     *        Looper,    。                  Looper         
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    
    //    
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//      
        mThread = Thread.currentThread();//      
    }
    
  • Looper.loop() Looperを動作させ、メッセージキューからメッセージを取り出し、メッセージを処理する.注意:Looper.loop()以降に書かれたコードは実行されません.この関数の内部はループであるべきです.mHandler.getLooper().quit()が呼び出されると、loopは中止され、その後のコードが実行されます.Looper.loop()ソースコード:
    /**
    * Run the message queue in this thread. Be sure to call
    * {@link #quit()} to end the loop.
    */
    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 (;;) {
           //             ,           queue.next()         
           //           Looper.quit()
           Message msg = queue.next(); 
           if (msg == null) {//                MessageQueue.quit();    MessageQueue.dispose()
               // No message indicates that the message queue is quitting.
               //Return here if the message loop has already quit and been disposed.
               return;
           }
           //msg.target  Handler  ,         
           msg.target.dispatchMessage(msg);
    
           // 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);
           }
           //Message    
           msg.recycleUnchecked();
       }
    }
    
    Looper.quit()ソースコード
    
    /**
    * Quits the looper.  (             )
    * 

    * Causes the {@link #loop} method to terminate without processing any * more messages in the message queue. *

    * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. *

    * Using this method may be unsafe because some messages may not be delivered * before the looper terminates. Consider using {@link #quitSafely} instead to ensure * that all pending work is completed in an orderly manner. *

    * * @see #quitSafely */ public void quit() { mQueue.quit(false); } /** * Quits the looper safely. ( ) *

    * Causes the {@link #loop} method to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * However pending delayed messages with due times in the future will not be * delivered before the loop terminates. *

    * Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. *

    */ public void quitSafely() { mQueue.quit(true); }
  • は、以上の知識に基づいて、プライマリスレッドがサブスレッド(非プライマリスレッド)にメッセージを送信することを実現することができる.次の例のmHandlerをクラスメンバーとして宣言し,メインスレッドでmHandlerを介してメッセージを送信すればよい.
    class LooperThread extends Thread  {  
            public Handler mHandler;  
            public void run()   {  
                Looper.prepare();  
                mHandler = new Handler()   {  
                    public void handleMessage(Message msg)   {  
                        // process incoming messages here  
                    }  
                };  
                
                //         UI   
                //1,Toast      
                //2,Dialog       
                //3,Snackbar   UI       ,   Looper.perpare().    Hander      Looper
                Looper.loop();  
                //   looper.quit()         
            }
        }