Handlerメカニズム解析


パフォーマンスの最適化を考慮すると、AndroidのUI操作はスレッドセキュリティではありません.これは、複数のスレッドがUIコンポーネントを同時に操作している場合、スレッドセキュリティの問題を引き起こす可能性があることを意味します.この問題を解決するために、Androidは、ActivityのUIコンポーネントをUIスレッド(すなわち、プライマリスレッド)のみで変更できるという簡単な原則を制定しました.
1つのプログラムが最初に起動されると、Androidは同時に1つのメインスレッドを起動し、メインスレッドは主にUIに関連するイベント、例えばユーザーのボタンイベント、ユーザーが画面に接触するイベント、スクリーン描画イベントを処理し、関連するイベントを対応するコンポーネントに配布して処理するので、メインスレッドは通常UIスレッドとも呼ばれる.
Handlerの概念:
1)計画タスクを実行し,所定の時間に特定のタスクを実行でき,タイマ2)スレッド間通信をシミュレートできる.Androidのアプリケーションが起動すると、メインスレッドが作成され、メインスレッドはメッセージキューを作成して様々なメッセージを処理します.サブスレッドを作成すると、親スレッドで作成したHandlerオブジェクトをサブスレッドから取得でき、親スレッドのメッセージキューにメッセージを送信できます.AndroidはUIスレッドでインタフェースを更新する必要があるため、この方法で他のスレッドでインタフェースを更新することができる.
Handlerクラスは、メッセージを送信、処理するための方法を含む.
  • void handlerMessage(Message msg):メッセージを処理する方法であり、この方法は通常書き換えに用いられる.
  • final boolean hasMessage(int what):メッセージキューにwhat属性が指定された値のメッセージが含まれているかどうかを確認します.
  • sendEmptyMessage(int what):空のメッセージを送信
  • final boolean sendMessage(Message msg):メッセージを直ちに送信し、この戻り値に注意し、messageがmessagequeueに正常に格納されるとtrueを返し、逆にfalseを返す.(個人的なアドバイス:このような問題については主観的に覚える必要はありません.実際に使用する場合は、ソースコードを直接表示すればいいです.ソースコードには詳細な注釈があります)
  • Handlerの役割:
    (1)メッセージを1つのスレッドで送信する.(2)メッセージを別のスレッドで取得・処理する.
    Handler処理の基本原理:
    メインスレッドがサブスレッドから送信されたメッセージをタイムリーに処理できるようにするには、コールバックの方法でしか実現できないことは明らかです.開発者はHandlerクラスのメソッドを書き換えるだけで、新しく起動したスレッドがメッセージを送信すると、それに関連するMessageQueueにメッセージが送信されます.一方、HandlerはMessageQuereからメッセージを取得して処理し続けます.これにより、Handlerクラスでメッセージを処理する方法がコールバックされます.
    スレッドでHandlerを使用するには、次のようにします.
    呼び出されたスレッドでは、(1)Looperを呼び出すprepare()メソッドが現在のスレッドに対してLooperオブジェクトを作成し、Looperオブジェクトを作成すると、そのコンストラクタがそれに対応するMessageQueueを作成します.(2)Looperができたら,Handlerサブクラスのインスタンスを作成し,HandlerMessage()メソッドを書き換え,他のスレッドからのメッセージの処理を担当する.(3)Looperを呼び出すloop()メソッドはLooperを起動する.
    注意:呼び出されたスレッドがメインスレッドクラスの場合、システムは自動的にメインスレッドにLooperのインスタンスを作成するので、第1、3のステップは省略できますが、第2のステップを行うだけでいいです.呼び出しスレッドで完了:(1)メッセージを作成し、内容を入力します.(2)呼び出しクラスによって作成されたHandlerインスタンスを用いてsendMessage(Message msg)メソッドを呼び出す.
    Handlerとスレッドの関係
    Handlerは一般的にメインスレッド内で実行され、サブスレッドで実行されますが、必ずLooperオブジェクトを作成します.メインスレッドのAndroidはすでにLooperオブジェクトを作成しています
    Handlerを使用する2つの一般的な状況:
    1.メインUIでのみUIを変更できます.しかし、実際には、一部のUIは、サブスレッドで修正ロジックを制御する必要があるため、サブスレッドはhandlerを介してメインスレッドにUIの変更を通知する必要がある.これは、新しいスレッドを周期的にUIを変更する必要があるなど、ゲーム開発で特によく見られる.サブスレッドはメインUIスレッドにUIコンポーネントの変更例を通知し、新しいスレッドは周期的にImageViewに表示される画像を変更する:この例はHandlerがメインスレッドで取得し、メッセージを処理し、サブスレッドでメッセージを送信する
    public class HandlerTest extends Activity {  
        //            ID 
        int[] imageIds = new int[]  
        {  
            R.drawable.java,  
            R.drawable.ee,  
            R.drawable.ajax,  
            R.drawable.xml,  
            R.drawable.classic  
        };  
        int currentImageId = 0;  
    
        @Override  
        public void onCreate(Bundle savedInstanceState)  
        {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
            final ImageView show = (ImageView) findViewById(R.id.show);  
            final Handler myHandler = new Handler()//     ,  ,    ,  UI  ,    UI   
            {  
                @Override  
                public void handleMessage(Message msg)  
                {  
                    //               
                    if (msg.what == 0x1233)  
                    {  
                        //             
                        show.setImageResource(imageIds[currentImageId++  
                            % imageIds.length]);  
                    }  
                }  
            };  
            //        ,               。          UI  ,        
            new Timer().schedule(new TimerTask()  
            {  
                @Override  
                public void run()  
                {  
                    //       
                    myHandler.sendEmptyMessage(0x1233);          
                }  
            }, 0, 1200);  
        }  
    }  

    2、ANRを避けるためには、サブスレッドで時間のかかる操作を行う必要がありますが、この操作が完了すると、メインスレッドにUIの変更を通知する必要がある場合があります.サブスレッドで時間のかかるタスクを実行した後、メインスレッドにUIコンポーネントを変更する例を通知します.新しいプロセスを使用して質量数を計算し、この例がメインスレッドでメッセージを送信し、サブスレッドで取得し、メッセージを処理することをToastで表示します.
    public class CalPrime extends Activity {  
        static final String UPPER_NUM = "upper";  
        EditText etNum;  
        CalThread calThread;  
        //         
        class CalThread extends Thread {  
            public Handler mHandler;  
    
            public void run()  
            {  
                Looper.prepare();//  Looper  ,      Handler      Looper   
                mHandler = new Handler()//      handler  ,     
                {  
                    //           
                    @Override  
                    public void handleMessage(Message msg)  
                    {  
                        if(msg.what == 0x123)  
                        {  
                            int upper = msg.getData().getInt(UPPER_NUM);  
                            List<Integer> nums = new ArrayList<Integer>();  
                            //    2  、 upper      
                            outer:  
                            for (int i = 2 ; i <= upper ; i++)  
                            {  
                                //  i   2  、 i         
                                for (int j = 2 ; j <= Math.sqrt(i) ; j++)  
                                {  
                                    //       ,          
                                    if(i != 2 && i % j == 0)  
                                    {  
                                        continue outer;  
                                    }  
                                }  
                                nums.add(i);  
                            }  
                            //   Toast            
                            Toast.makeText(CalPrime.this , nums.toString()  
                                , Toast.LENGTH_LONG).show();  
                        }  
                    }  
                };  
                Looper.loop();//  Looper 
            }  
        }  
        @Override  
        public void onCreate(Bundle savedInstanceState)  
        {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
            etNum = (EditText)findViewById(R.id.etNum);  
            calThread = new CalThread();  
            //       
            calThread.start();  
        }  
        //                  
        public void cal(View source)  
        {  
            //      
            Message msg = new Message();  
            msg.what = 0x123;  
            Bundle bundle = new Bundle();  
            bundle.putInt(UPPER_NUM ,  
                Integer.parseInt(etNum.getText().toString()));  
            msg.setData(bundle);  
            //            Handler     
            calThread.mHandler.sendMessage(msg);//          
        }  
    }  

    Handler,Loop,Message,MessageQueueの動作原理
    Loop,Message,MessageQueue概念理解:
  • Message:Handlerが送信、受信、および処理するメッセージオブジェクト
  • Looper:各スレッドに1つのLooperしか持たない.そのlooper()メソッドは、MessageQueue内のメッセージをループして読み取り、そのメッセージを送信するhandlerに渡して処理する.
  • MessageQueue:Messageを管理するために先進的な方法を採用したメッセージキュー.プログラムはLooperオブジェクトを作成すると、そのコンストラクタにMessageQueue
  • が作成されます.
    Looperが提供するソースコードは以下の通りです.
    private Looper(boolean quitAllowed) {  
          Queue = new MessageQueue(quitAllowed);  
          mThread = Thread.currentThread();  
    }  

    ソースコードの2行目から、Looperオブジェクトの作成時に関連付けられたMessageQueueオブジェクトが作成されることがわかります.コンストラクタはprivate修飾なので、プログラマはLooperオブジェクトを作成できません.つまり、Looperを作成すると同時にMessageQueueオブジェクトが作成されます.
    Handler:Handlerの役割は2つあります.メッセージの送信とメッセージの処理です.Handlerが送信したメッセージは指定したMessageQueueに送らなければなりません.つまり、Handlerが正常に動作するには、現在のスレッドにMessageQueueが1つある必要があります.そうしないと、メッセージは保存されません.MessageQueueはLooperが管理するため、Handlerが正常に動作するには、現在のスレッドにLooperオブジェクトが1つある必要があります.ここでは2つのケースに分けられます.
  • メインスレッド(UIスレッド)、システムはすでにLooperオブジェクトを初期化しているので、プログラムは直接Handlerを作成すれば
  • になる.
  • プログラマが独自に作成したサブスレッドです.この場合、プログラマはLooperオブジェクトを作成し、起動する必要があります.

  • Looperの作成:
    Looperを使用prepare()、ソースコードの表示
    public static void prepare() {  
        prepare(true);  
    }  
    
    private static void prepare(boolean quitAllowed) {  
        if (sThreadLocal.get() != null) {  
             throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper(quitAllowed));  
    }  
    
    private Looper(boolean quitAllowed) {  
        mQueue = new MessageQueue(quitAllowed);  
        mThread = Thread.currentThread();  
    }  

    メソッド呼び出しにより、9行目にLooperオブジェクトが作成され、Looperオブジェクトが作成されると同時にMessageQueueオブジェクトが作成されます(13行目).さらに、prepare()は、1つのスレッドに対して最大1つのLooperが作成されることを可能にしていることがわかる.
    Looperの起動:
    Looper.loop(),loop()は,デッドループを用いてMessageQueue内のメッセージを絶えず取り出し,対応するHandlerにメッセージを送信して処理する.次はLooperクラスにおけるlooper()メソッドの一部のソースコードです.
    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);  
          }  
    
          // 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.recycleUnchecked();  
    }  

    明らかに1行目はデッドサイクルを用い,2行目はqueueからMessageを取り出し,15行目はdispatchMessage(Message msg)法によりHandlerにメッセージを送信する.
    Looper,MessageQueue,Handlerのそれぞれの役割は以下の通りである.
    Looper:スレッドごとに1つのLooperしかありません.彼はMessageQueueを管理しています.MessageQueueからメッセージを取り出し続け、対応するHandler処理MessageQueueにメッセージを渡します.Looperが管理しています.スレッドが入ったメッセージを保存するために使用されます.Handler:Looperが管理するMessageQueueにメッセージを送信し、Looperが割り当てたメッセージの処理を担当します.
    スレッド内のHandlerの使用手順は次のとおりです.
    (1)Looperを呼び出すprepare()メソッド現在のスレッドに対してLooperオブジェクトを作成し,Looperオブジェクトを作成するとそのコンストラクタがそれに対応するMessageQueueを作成する.(2)Looperができたら,Handlerサブクラスのインスタンスを作成し,HandlerMessage()メソッドを書き換え,他のスレッドからのメッセージの処理を担当する.(3)Looperを呼び出すloop()メソッドはLooperを起動する.
    転載:http://blog.csdn.net/tuke_tuke/article/details/50782893 http://blog.csdn.net/tuke_tuke/article/details/50783153