Androidメッセージメカニズムを深く分析する

10312 ワード

Androidでは、スレッドの内部やスレッド間で情報のインタラクションを行う際によくメッセージが使用されます.これらの基礎的なものは、内部の原理を熟知すれば、システムを容易に、よりよく構築し、低レベルのエラーを避けることができます.Androidのメッセージメカニズムを学ぶ前に、メッセージに関連するいくつかのクラスを理解します.
1.Message
メッセージオブジェクトは,その名の通りメッセージ情報を記録するクラスである.このクラスにはいくつかの重要なフィールドがあります.
a.arg 1とarg 2:転送する整数値を格納するために2つのフィールドを使用できます.サービスでは、サービスIDを格納するために使用できます.
b.obj:このフィールドはObjectタイプで、メッセージの受信者に複数のフィールドを渡すことができます.
c.what:このフィールドはメッセージのフラグと言えます.メッセージ処理では、Buttonイベントを処理するときに、どのボタンをクリックしたかをswitch(v.getId()で判断するのと同じように、このフィールドの異なる値に基づいて異なる処理を行うことができます.
Messageを使用する場合、new Message()でMessageインスタンスを作成できますが、AndroidではMessageを使用することをお勧めします.Obtain()またはHandler.obtainMessage()は、Messageオブジェクトを取得します.これは、必ずしも新しいインスタンスを直接作成するわけではありません.メッセージ・プールから使用可能なMessageインスタンスがあるかどうかを確認し、存在する場合は、このインスタンスを直接取り出して返します.逆に、メッセージ・プールに使用可能なMessageインスタンスがない場合は、指定されたパラメータnewに基づいて新しいMessageオブジェクトが作成されます.ソースコードを解析すると、Androidシステムはデフォルトでメッセージプールに10個のMessageオブジェクトをインスタンス化していることがわかります.
2.MessageQueue
メッセージキューは、Messageオブジェクトのデータ構造を格納し、メッセージを「先進先出」の原則に従って格納します.保存は実際の意味での保存ではなく、Messageオブジェクトをチェーンテーブルで直列に接続します.MessageQueueオブジェクトは、私たちが自分で作成する必要はありません.Looperオブジェクトが管理しています.スレッドは最大1つのMessageQueueしか持てません.私たちはLooperを通じてmyQueue()は、現在のスレッドのMessageQueueを取得します.
3.Looper
MessageQueueの管理者は、1つのスレッドにおいてLooperオブジェクトが存在する場合、必ずMessageQueueオブジェクトが存在し、1つのLooperオブジェクトと1つのMessageQueueオブジェクトのみが存在する.Androidシステムでは、メインスレッドにデフォルトのLooperオブジェクトがあるほか、他のスレッドにはLooperオブジェクトがないのがデフォルトです.新しく作成したスレッドにLooperオブジェクトを持たせる場合は、まずLooperを呼び出す必要があります.prepare()メソッドを呼び出し、Looper.を呼び出します.loop()メソッド.典型的な使い方は以下の通りです.
class LooperThread extends Thread
{
    public Handler mHandler;
    public void run()
    {
        Looper.prepare();
        //         
        Looper.loop();
    }
}

我々のスレッドにLooperオブジェクトが存在する場合、Looper.myLooper()を取得し、Looper.を通じてgetMainLooper()は、現在のアプリケーションシステムのメインスレッドのLooperオブジェクトを取得します.ここで注意すべき点は、Looperオブジェクトがアプリケーションのメインスレッドにある場合、Looper.myLooper()とLooper.getMainLooper()は同じオブジェクトを取得します.
4.Handler
メッセージの処理者.HandlerオブジェクトによってMessageオブジェクトをカプセル化し、sendMessage(msg)によってMessageオブジェクトをMessage Queueに追加することができます.Message QueueがMessageにループすると、Messageオブジェクトに対応するhandlerオブジェクトのhandleMessage()メソッドが呼び出されて処理されます.メッセージはhandleMessage()メソッドで処理されるため、Handlerから継承されたクラスを記述し、handleMessage()で必要な操作を処理する必要があります.
次に、Androidでメッセージをどのように処理するかを追跡コードで分析します.まず、テストコードを貼り付けます.
/**
* 
* @author coolszy
* @blog http://blog.csdn.net/coolszy
*
*/
public class MessageService extends Service
{
    private static final String TAG = "MessageService";
    private static final int KUKA = 0;

    private Looper looper;
    private ServiceHandler handler;

    /**
     *         Handler handleMessage()   ,           
     *    Handler ,   handleMessage()             
     * @author coolszy
     *
     */
    private final class ServiceHandler extends Handler
    {
        public ServiceHandler(Looper looper)
        {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg)
        {
            //   what         
            switch (msg.what)
            {
            case KUKA:
                //  msg obj  。                 
                Log.i(TAG, "The obj field of msg:" + msg.obj);
                break;
            // other cases
            default:
                break;
            }
            //     Service     ,   Service
            stopSelf(msg.arg1);
        }
    }

    @Override
    public void onCreate()
    {
        Log.i(TAG, "MessageService-->onCreate()");

        //      Service        ,            ,  
        //       ,            ,   Service
        //             
        HandlerThread thread = new HandlerThread("MessageDemoThread", Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();

        //         looper  
        looper = thread.getLooper();
        //  Handler  , looper      handler、
        //looper messageQueue      
        handler = new ServiceHandler(looper);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        Log.i(TAG, "MessageService-->onStartCommand()");

        //         Message  
        Message msg = handler.obtainMessage();
        // arg1     ID, handleMessage()   
        //       stopSelf(startId)  ,    
        msg.arg1 = startId;
        // msg   
        msg.what = KUKA;
        //         date  ,   obj  
        //           obj           
        Date date = new Date();
        msg.obj = date;
        //  msg   MessageQueue 
        handler.sendMessage(msg);

        return START_STICKY;
    }

    @Override
    public void onDestroy()
    {
        Log.i(TAG, "MessageService-->onDestroy()");
    }

    @Override
    public IBinder onBind(Intent intent)
    {
        return null;
    }
}

注意:テストコードではHandlerThreadクラスを使用しています.このクラスはThreadのサブクラスです.このクラスは実行時にlooperオブジェクトを作成します.このクラスを使用すると、自分でThreadサブクラスを作成し、Looperを作成する手間が省けます.
次に、プログラムの実行手順を分析します.
1.onCreate()
まずサービスを開始するとonCreate()メソッドが呼び出されます.このメソッドでは、スレッドの名前と優先度を提供するHandlerThreadオブジェクトをnewします.
次にstart()メソッドを呼び出し、HandlerThreadオブジェクトのrun()メソッドを呼び出します.
public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

run()メソッドでは、システムがスレッドに追加したLooperを呼び出し、同時にLooperのloop()メソッドを呼び出します.
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();
            }
        }
    }

ソースコードからloop()メソッドがデッドループであることがわかり、MessageQueueオブジェクトからMessageオブジェクトを取得し続け、MessageQueueオブジェクトにMessageオブジェクトが存在しない場合は、今回のループを終了し、ループを継続します.Messageオブジェクトが存在する場合はmsgを実行する.target.dispatchMessage(msg)ですが、このmsgです.targetフィールドの値は何ですか?まず、ソースコードの追跡を一時的に停止し、onCreate()メソッドに戻ります.スレッドがstart()メソッドを実行した後、スレッドのLooperオブジェクトを取得し、newのServiceHandlerオブジェクトをnewします.LooperオブジェクトをServiceHandlerコンストラクション関数に渡すと、handler、looper、messageQueueの3つが連絡を取ります.
2.onStartCommand()
onStart()メソッドを実行すると、onStart Command()メソッドが実行されます.まず、メッセージプールからMessageインスタンスを取得し、Messageオブジェクトのarg 1、what、objの3つのフィールドに値を割り当てます.次にsendMessage(msg)メソッドを呼び出すと、sendMessageDelayed(msg,0)メソッドが呼び出され、sendMessageDelayed()メソッドはsendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis)メソッドが呼び出されます.このメソッドでは、文コードmsgに注意してください.target=this、msgのtargetはthisを指し、thisはServiceHandlerオブジェクトであるため、msgのtargetフィールドはServiceHandlerオブジェクトを指し、このメソッドはMessageQueueのenqueueMessage(msg、uptimeMillis)メソッドを呼び出す.
final boolean enqueueMessage(Message msg, long when) {
        if (msg.when != 0) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                this.notify();
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                this.notify();
            }
        }
        return true;
    }

この方法の主なタスクはMessageオブジェクトをMessageQueueに追加することである(データ構造の最も基礎的なものは,自分で絵を描いて理解する).
handler.sendMessage()-->handler.sendMessageDelayed()-->handler.sendMessageAtTime()-->msg.target = this;queue.EnqueueMessage=>>メッセージキューにmsgを追加
3.handleMessage(msg)
onStartCommand()の実行が完了すると、私たちのServiceのメソッドが実行されます.handleMessage()はどのように呼び出されますか?前に解析したloop()メソッドでは,msgのtargetフィールドコードが何であるかを知らなかったが,上の解析でServiceHandlerオブジェクト,msgを表すことが分かった.target.dispatchMessage(msg);ServiceHandlerオブジェクトのdispatchMessage()メソッドを実行することを示します.
public void dispatchMessage(Message msg) {         if (msg.callback != null) {             handleCallback(msg);         } else {             if (mCallback != null) {                 if (mCallback.handleMessage(msg)) {                     return;                 }             }             handleMessage(msg);         }     }
このメソッドは、まず、callbackが空であるかどうかを判断し、追跡中に付与されていないため、callbackフィールドが空であるため、最終的にはhandleMessage()メソッド、すなわち、ServiceHandlerクラスでの複写メソッドが実行されます.このメソッドではwhatフィールドの値に基づいて、どのコードが実行されるかを判断します.
これで,一つのMessageがHandlerを経由して送信され,MessageQueueの入隊,Looperの抽出が再びHandlerの懐に戻ってくるのを見た.この一周は、同期操作を非同期操作にするのに役立ちます.
すべてのテストコードは次のとおりです.
http://u.115.com/file/f1e0a5d5db
http://www.cmd100.com/bbs/thread-147897-1-1.html