Android 消息處理機制


1.消息處理機制簡介

主要用於進程內線程之間的通信,主線程一般調用looper()進行循環等待處理消息,其它線程向它發消息並指定消息的處理方法。

(1)涉及文件包括frameworks中的:
Looper.java
Handler.java
MessageQueue.java
Message.java
android_os_MessageQueue.cpp
...
涉及文件包括system中的:
Looper.h
Looper.cpp

(2)涉及native處理機制
使用pipe加epoll機制實現消息隊列的睡眠和喚醒進程的功能,同時利用epoll的timeout特性可以指定消息延期多長時間后執行。

 

2.消息發送流程

enqueueMessage    //MessageQueue.java
    nativeWake(mPtr);
        android_os_MessageQueue_nativeWake //android_os_MessageQueue.cpp
            nativeMessageQueue->wake()
            wake() //system/core/libutils/Looper.cpp
                 write(mWakeWritePipeFd, "W", 1); //只是向消息隊列的寫端寫入一個字符喚醒epoll_wait監聽而已

從發送流程可以看出:
Framework中的sendMessage其實並不是通過管道傳輸的,它只是把msg放到Looper的消息隊列上,獲取消息的時候是直接從消息隊列上獲取的,
整個消息傳送的過程根本不涉及管道傳輸數據。native方法中使用pipe進行epoll的作用僅僅是想讓java的隊列具有睡眠喚醒功能。

 

3.消息獲取流程

loop() //Looper.java
    queue.next() //MessageQueue.java
        nativePollOnce(ptr, nextPollTimeoutMillis); //會阻塞在這個函數它,它最終調用的是epoll_wait()進行阻塞。
            android_os_MessageQueue_nativePollOnce //android_os_MessageQueue.cpp
                pollOnce(int timeoutMillis) //system/core/include/utils/Looper.h
                    pollInner //system/core/libutils/Looper.cpp
                        epoll_wait(..., timeoutMillis); //調用系統的epoll_wait函數並設置等待時間為timeoutMillis

 

4.消息的處理流程

Handler.java中的dispatchMessage方法:

public void dispatchMessage(Message msg) {
    //優先調用callback,注意它是一個Runnable線程!!執行結果就是啟動這個線程去執行。
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //否則若是創建handler的時候指定了callback,就調用這個callback的handleMessage()進行處理
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //否則就調用handler的它,它是個空函數,但是可以被重寫
        handleMessage(msg);
    }
}
View Code

 

5.實驗Demo

下面的例子是分別使用這三種處理msg的方法的Demo,在MainActivity.java中實現的

package com.example.mm.app_message_01;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MessageTest";
    private Button mButton = null;
    private int mClickedCount = 0;
    private int getMessageCount1 = 0;
    private int getMessageCount2 = 0;
    private int getMessageCount3 = 0;

    /* Test1: success */
    private MyThread mThread1 = null;
    private Handler mHandler1 = null;

    private MyThread mThread2 = null;
    private Handler mHandler2 = null;

    private MyThread mThread3 = null;
    private Handler mHandler3 = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);


        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Code here executes on main thread after user presses button
                Log.d(TAG, "Clicked: " + mClickedCount);
                mClickedCount++;

                mHandler1.sendMessage(new Message());

                mHandler2.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.d(TAG, "-2-: Get a new Message, getMessageCount = " + getMessageCount2);
                        getMessageCount2++;
                    }
                });

                mHandler3.sendMessage(new Message());
            }
        });

        /*----------------------test1------------------------*/
        mThread1 = new MyThread();
        mThread1.start();
        mHandler1 = new Handler(mThread1.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                Log.d(TAG, "-1-: Get a new Message, getMessageCount = " + getMessageCount1);
                getMessageCount1++;
                return true;
            }
        });

        /*----------------------test2------------------------*/
        mThread2 = new MyThread();
        mThread2.start();
        mHandler2 = new Handler(mThread2.getLooper());

        /*----------------------test3------------------------*/
        mThread3 = new MyThread();
        mThread3.start();
        mHandler3 = new MyHandler(mThread3.getLooper());
    }

    class MyThread extends Thread {
        private Looper mLooper;

        public void run() {
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Looper.loop();
        }
        Looper getLooper() {
            if (!isAlive()) {
                return null;
            }
            synchronized (this) {
                if (mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            return mLooper;
        }
    }

    class MyHandler extends Handler {
        public MyHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "-3-: Get a new Message, getMessageCount = " + getMessageCount3);
            getMessageCount3++;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
View Code

 測試結果:

/*每當點擊屏幕上的Button都會打印下面一組log:*/
01-01 13:15:22.310 13412-13412/com.example.mm.app_message_01 D/MessageTest: Clicked: 14
01-01 13:15:22.312 13412-13431/com.example.mm.app_message_01 D/MessageTest: -1-: Get a new Message, getMessageCount = 14
01-01 13:15:22.312 13412-13432/com.example.mm.app_message_01 D/MessageTest: -2-: Get a new Message, getMessageCount = 14
01-01 13:15:22.312 13412-13433/com.example.mm.app_message_01 D/MessageTest: -3-: Get a new Message, getMessageCount = 14
View Code

 

6.native的實現很復雜,好像是鏡面映射了一個message的queue和java的queue對應。

7.Looper類中只有一個構造函數,它里面new了一個MessageQueue,也就是說MessageQueue是Looper對象的。

 

 

 

 

 

參考:

android消息處理機制原理解析:https://blog.csdn.net/chunqiuwei/article/details/52251242

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM