Android消息機(jī)制

3.3 Android消息機(jī)制

3.3.1 概述

Android有大量的消息驅(qū)動(dòng)方式來進(jìn)行交互,比如Android的四大組件Activity叮叹、Service均唉、Broadcast和ContentProvider的啟動(dòng)過程的交互钧大,都離不開消息機(jī)制朽褪,Android某種意義上也可以說成是一個(gè)以消息驅(qū)動(dòng)的系統(tǒng)辱挥。消息機(jī)制涉及MessageQueue/Message/Looper/Handler這4個(gè)類犁嗅。

消息模型

Android消息機(jī)制主要包含:

  1. Message:消息分為硬件產(chǎn)生的消息(如按鈕、觸摸)和軟件生成的消息晤碘。
  2. MessageQueue:消息隊(duì)列的主要功能向消息池投遞消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)褂微。
  3. Looper:不斷循環(huán)執(zhí)行(Looper.loop),按分發(fā)機(jī)制將消息分發(fā)給目標(biāo)處理者园爷。
  4. Handler:消息輔助類宠蚂,主要功能向消息池發(fā)送各種消息事件(Handler.sendMessage)和處理相應(yīng)消息事件(Handler.handleMessage)。

其基本結(jié)構(gòu)如下所示:

其中:Looper有一個(gè)MessageQueue消息隊(duì)列童社,MessageQueue有一組待處理的Message求厕,Message中有一個(gè)用于處理消息的Handler,Handler中有Looper和MessageQueue扰楼。

3.3.2 Looper

我們先看一個(gè)關(guān)于Handler/Looper的使用例子:

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare();   //創(chuàng)建looper
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                ... //消息處理邏輯
            }
        };
        Looper.loop();  //開始looper循環(huán)
    }
}

接下來我們將圍繞這個(gè)實(shí)例展開詳細(xì)分析:

Looper.prepare()

Looper.prepare()在每個(gè)線程只允許執(zhí)行一次呀癣,該方法會(huì)創(chuàng)建Looper對(duì)象,Looper的構(gòu)造方法中會(huì)創(chuàng)建一個(gè)MessageQueue對(duì)象弦赖,再將Looper對(duì)象保存到當(dāng)前線程TLS项栏。對(duì)于無參的情況,默認(rèn)調(diào)用prepare(true)腾节,表示的是這個(gè)Looper允許退出忘嫉,而對(duì)于false的情況則表示當(dāng)前Looper不允許退出。

private static void prepare(boolean quitAllowed) {
    //每個(gè)線程只允許執(zhí)行一次該方法案腺,第二次執(zhí)行時(shí)線程的TLS已有數(shù)據(jù)庆冕,則會(huì)拋出異常
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //創(chuàng)建Looper對(duì)象,并保存到當(dāng)前線程的TLS區(qū)域
    sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  //創(chuàng)建MessageQueue對(duì)象
    mThread = Thread.currentThread();  //記錄當(dāng)前線程
}

另外劈榨,與prepare()相近功能的访递,還有一個(gè)prepareMainLooper()方法,該方法主要在ActivityThread類中使用同辣。這里的sThreadLocal是ThreadLocal類型拷姿,下面先說說ThreadLocal:

ThreadLocal

ThreadLocal是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類惭载,通過它可以在指定的線程中存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后响巢,只有在指定線程中可以獲取到存儲(chǔ)的數(shù)據(jù)描滔,對(duì)于其他線程來說則無法獲取到數(shù)據(jù)。一般來說踪古,當(dāng)某些數(shù)據(jù)是以線程為作用域并且不同線程具有不同的數(shù)據(jù)副本的時(shí)候含长,可以考慮使用ThreadLocal。對(duì)于Handler來說伏穆,它需要獲取當(dāng)前線程的Looper拘泞,而Looper的作用域就是線程并且不同線程具有不同的Looper,這個(gè)時(shí)候通過ThreadLocal就可以實(shí)現(xiàn)Looper在線程中的存取了枕扫。

ThreadLocal的原理:不同線程訪問同一個(gè)ThreadLocal的get方法時(shí)陪腌,ThreadLocal內(nèi)部會(huì)從各自的線程中取出一個(gè)數(shù)組,然后再從數(shù)組中根據(jù)當(dāng)前ThreadLocal的索引去查找出對(duì)應(yīng)的value值烟瞧,不同線程中的數(shù)組是不同的诗鸭。我們看一下它的set方法和get方法:

public void set(T value) {
    Thread currentThread = Thread.currentThread(); //獲取當(dāng)前線程
    Values values = values(currentThread); //查找當(dāng)前線程的本地儲(chǔ)存區(qū)
    if (values == null) {
        //當(dāng)線程本地存儲(chǔ)區(qū),尚未存儲(chǔ)該線程相關(guān)信息時(shí)燕刻,則創(chuàng)建Values對(duì)象
        values = initializeValues(currentThread);
    }
    //保存數(shù)據(jù)value到當(dāng)前線程this
    values.put(this, value);
}
public T get() {
    Thread currentThread = Thread.currentThread(); //獲取當(dāng)前線程
    Values values = values(currentThread); //查找當(dāng)前線程的本地儲(chǔ)存區(qū)
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1]; //返回當(dāng)前線程儲(chǔ)存區(qū)中的數(shù)據(jù)
        }
    } else {
        //創(chuàng)建Values對(duì)象
        values = initializeValues(currentThread);
    }
    return (T) values.getAfterMiss(this); //從目標(biāo)線程存儲(chǔ)區(qū)沒有查詢是則返回null
}

Values類是Thread類內(nèi)部專門用來存儲(chǔ)線程的ThreadLocal數(shù)據(jù)的只泼,它內(nèi)部有一個(gè)數(shù)組table剖笙,ThreadLocal的值就存在這個(gè)table數(shù)組中卵洗。如果values的值為null,那么就需要對(duì)其進(jìn)行初始化然后再將ThreadLocal的值進(jìn)行存儲(chǔ)弥咪。而ThreadLocal數(shù)據(jù)的存儲(chǔ)規(guī)則:ThreadLocal的值在table數(shù)組中的存儲(chǔ)位置總是ThreadLocal的索引+1的位置过蹂。

loop()

public static void loop() {
    final Looper me = myLooper();  //獲取TLS存儲(chǔ)的Looper對(duì)象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;  //獲取Looper對(duì)象中的消息隊(duì)列

    Binder.clearCallingIdentity();
    //確保在權(quán)限檢查時(shí)基于本地進(jìn)程,而不是基于最初調(diào)用進(jìn)程聚至。
    final long ident = Binder.clearCallingIdentity();

    for (;;) { //進(jìn)入loop的主循環(huán)方法
        Message msg = queue.next(); //可能會(huì)阻塞
        if (msg == null) { //沒有消息酷勺,則退出循環(huán)
            return;
        }
        ...
        msg.target.dispatchMessage(msg); //用于分發(fā)Message
        ...
        final long newIdent = Binder.clearCallingIdentity(); //確保分發(fā)過程中identity不會(huì)損壞
        if (ident != newIdent) {
             //打印identity改變的log,在分發(fā)消息過程中是不希望身份被改變的扳躬。
        }
        msg.recycleUnchecked();  //將Message放入消息池
    }
}

loop()進(jìn)入循環(huán)模式脆诉,這是消息處理的核心部分:不斷重復(fù)下面的操作,直到?jīng)]有消息時(shí)退出循環(huán):

  1. 讀取MessageQueue的下一條Message贷币;
  2. 把Message分發(fā)給相應(yīng)的target击胜,而這個(gè)target就是發(fā)送這條消息的Handler對(duì)象;
  3. 再把分發(fā)后的Message回收到消息池役纹,以便重復(fù)利用偶摔。

quit()

Looper的quit和quitSafely方法的區(qū)別是:前者會(huì)直接退出Looper,后者設(shè)定一個(gè)退出標(biāo)記促脉,然后把消息隊(duì)列中的已有消息處理完畢后才安全地退出辰斋。Looper退出之后策州,通過Handler發(fā)送的消息就會(huì)失敗,這個(gè)時(shí)候Handler的send方法會(huì)返回false宫仗。quit()方法的實(shí)現(xiàn)最終調(diào)用的是MessageQueue.quit()方法:

void quit(boolean safe) {
    // 當(dāng)mQuitAllowed為false够挂,表示不運(yùn)行退出,強(qiáng)行調(diào)用quit()會(huì)拋出異常
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    synchronized (this) {
        if (mQuitting) { //防止多次執(zhí)行退出操作
            return;
        }
        mQuitting = true;
        if (safe) {
            removeAllFutureMessagesLocked(); //移除尚未觸發(fā)的所有消息
        } else {
            removeAllMessagesLocked(); //移除所有的消息
        }
        //mQuitting=false藕夫,那么認(rèn)定為 mPtr != 0
        nativeWake(mPtr);
    }
}

消息退出的方式:當(dāng)safe=true時(shí)下硕,只移除尚未觸發(fā)的所有消息,對(duì)于正在觸發(fā)的消息并不移除汁胆;當(dāng)safe=flase時(shí)梭姓,移除所有的消息。

在子線程中嫩码,如果手動(dòng)為其創(chuàng)建了Looper誉尖,那么在所有的事情完成以后應(yīng)該調(diào)用quit方法來終止消息循環(huán),否則這個(gè)子線程就會(huì)一直處于等待的狀態(tài)铸题。

3.3.3 Handler

Handler類在構(gòu)造方法中铡恕,可指定Looper、Callback回調(diào)方法以及消息的處理方式(同步或異步)丢间,對(duì)于無參的handler探熔,默認(rèn)是當(dāng)前線程的Looper。具體代碼如下所示:

public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
    //匿名類烘挫、內(nèi)部類或本地類都必須申明為static诀艰,否則會(huì)警告可能出現(xiàn)內(nèi)存泄露
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    //必須先執(zhí)行Looper.prepare(),才能獲取Looper對(duì)象饮六,否則為null.
    mLooper = Looper.myLooper();  //從當(dāng)前線程的TLS中獲取Looper對(duì)象
    if (mLooper == null) {
        throw new RuntimeException("");
    }
    mQueue = mLooper.mQueue; //消息隊(duì)列其垄,來自Looper對(duì)象
    mCallback = callback;  //回調(diào)方法
    mAsynchronous = async; //設(shè)置消息是否為異步處理方式
}

消息分發(fā)機(jī)制

在Looper.loop()中,當(dāng)發(fā)現(xiàn)有消息時(shí)卤橄,調(diào)用消息的目標(biāo)handler绿满,執(zhí)行dispatchMessage()方法來分發(fā)消息。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //當(dāng)Message存在回調(diào)方法窟扑,回調(diào)msg.callback.run()方法喇颁;
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //當(dāng)Handler存在Callback成員變量時(shí),回調(diào)方法handleMessage()嚎货;
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //Handler自身的回調(diào)方法handleMessage()
        handleMessage(msg);
    }
}

分發(fā)消息流程:

  1. 當(dāng)Message的回調(diào)方法不為空時(shí)橘霎,則回調(diào)方法msg.callback.run(),其中callBack數(shù)據(jù)類型為Runnable厂抖,否則進(jìn)入步驟2茎毁;
  2. 當(dāng)Handler的mCallback成員變量不為空時(shí),則回調(diào)方法mCallback.handleMessage(msg),否則進(jìn)入步驟3七蜘;
  3. 調(diào)用Handler自身的回調(diào)方法handleMessage()谭溉,該方法默認(rèn)為空,Handler子類通過覆寫該方法來完成具體的業(yè)務(wù)邏輯橡卤。

對(duì)于很多情況下扮念,消息分發(fā)后的處理方法是第3種情況,即Handler.handleMessage()碧库,一般覆寫該方法從而實(shí)現(xiàn)自己的業(yè)務(wù)邏輯柜与。

消息發(fā)送

發(fā)送消息調(diào)用鏈:

從上圖,可以發(fā)現(xiàn)所有的消息發(fā)送方式嵌灰,最終都是調(diào)用MessageQueue.enqueueMessage()弄匕,將消息添加到消息隊(duì)列中;

Handler是消息機(jī)制中非常重要的輔助類,更多的實(shí)現(xiàn)都是MessageQueue和Message中的方法沽瞭,Handler的目的是為了更加方便的使用消息機(jī)制迁匠,比如將一個(gè)任務(wù)切換到某個(gè)指定的線程中去執(zhí)行,Android中的UI操作只能在主線程中進(jìn)行驹溃,ViewRootImpl的checkThread方法會(huì)驗(yàn)證當(dāng)前線程是否可以進(jìn)行UI操作城丧。

3.3.4 MessageQueue

MessageQueue是消息機(jī)制的Java層和C++層的連接紐帶,大部分核心方法都交給native層來處理豌鹤,其中MessageQueue類中涉及的native方法如下:

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

之后我們會(huì)簡單介紹這些native方法亡哄。

創(chuàng)建MessageQueue

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    //通過native方法初始化消息隊(duì)列,其中mPtr是供native代碼使用
    mPtr = nativeInit();
}

提取消息:next()

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) { //當(dāng)消息循環(huán)已經(jīng)退出布疙,則直接返回
        return null;
    }
    int pendingIdleHandlerCount = -1; // 循環(huán)迭代的首次為-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞操作蚊惯,當(dāng)?shù)却齨extPollTimeoutMillis時(shí)長,或者消息隊(duì)列被喚醒拐辽,都會(huì)返回
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //當(dāng)消息Handler為空時(shí)拣挪,查詢MessageQueue中的下一條異步消息msg擦酌,則退出循環(huán)俱诸。
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    //當(dāng)異步消息觸發(fā)時(shí)間大于當(dāng)前時(shí)間,則設(shè)置下一次輪詢的超時(shí)時(shí)長
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 獲取一條消息赊舶,并返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //設(shè)置消息的使用狀態(tài)睁搭,即flags |= FLAG_IN_USE
                    msg.markInUse();
                    return msg;   //成功地獲取MessageQueue中的下一條即將要執(zhí)行的消息
                }
            } else {
                //沒有消息
                nextPollTimeoutMillis = -1;
            }
            //消息正在退出,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            //當(dāng)消息隊(duì)列為空笼平,或者是消息隊(duì)列的第一個(gè)消息時(shí)
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                //沒有idle handlers 需要運(yùn)行园骆,則循環(huán)并等待。
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        //只有第一次循環(huán)時(shí)寓调,會(huì)運(yùn)行idle handlers锌唾,執(zhí)行完成后,重置pendingIdleHandlerCount為0.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; //去掉handler的引用
            boolean keep = false;
            try {
                keep = idler.queueIdle();  //idle時(shí)執(zhí)行的方法
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        //重置idle handler個(gè)數(shù)為0,以保證不會(huì)再次重復(fù)運(yùn)行
        pendingIdleHandlerCount = 0;
        //當(dāng)調(diào)用一個(gè)空閑handler時(shí)晌涕,一個(gè)新message能夠被分發(fā)滋捶,因此無需等待可以直接查詢pending message.
        nextPollTimeoutMillis = 0;
    }
}

nativePollOnce是一個(gè)native方法,其是阻塞操作余黎,當(dāng)nativePollOnce()返回后重窟,next()從mMessages中提取一個(gè)消息。其中nextPollTimeoutMillis代表下一個(gè)消息到來前惧财,還需要等待的時(shí)長巡扇;當(dāng)nextPollTimeoutMillis = -1時(shí),表示消息隊(duì)列中無消息垮衷,會(huì)一直等待下去厅翔。當(dāng)處于空閑時(shí),往往會(huì)執(zhí)行IdleHandler中的方法搀突。

添加消息:enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
    // 每一個(gè)Message必須有一個(gè)target
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    synchronized (this) {
        if (mQuitting) {  //正在退出時(shí)知给,回收msg,加入到消息池
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            //p為null(代表MessageQueue沒有消息) 或者msg的觸發(fā)時(shí)間是隊(duì)列中最早的描姚, 則進(jìn)入該該分支
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; //當(dāng)阻塞時(shí)需要喚醒
        } else {
            //將消息按時(shí)間順序插入到MessageQueue涩赢。一般地,不需要喚醒事件隊(duì)列轩勘,除非
            //消息隊(duì)頭存在barrier筒扒,并且同時(shí)Message是隊(duì)列中最早的異步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }
        //消息沒有退出绊寻,我們認(rèn)為此時(shí)mPtr != 0
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

MessageQueue是按照Message觸發(fā)時(shí)間的先后順序排列的花墩,隊(duì)頭的消息是將要最早觸發(fā)的消息。當(dāng)有消息需要加入消息隊(duì)列時(shí)澄步,會(huì)從隊(duì)列頭開始遍歷冰蘑,直到找到消息應(yīng)該插入的合適位置,以保證所有消息的時(shí)間順序村缸。

移除消息:removeMessages()

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }
    synchronized (this) {
        Message p = mMessages;
        //從消息隊(duì)列的頭部開始祠肥,移除所有符合條件的消息
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }
        //移除剩余的符合要求的消息
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

這個(gè)移除消息的方法,采用了兩個(gè)while循環(huán)梯皿,第一個(gè)循環(huán)是從隊(duì)頭開始仇箱,移除符合條件的消息,第二個(gè)循環(huán)是從頭部移除完連續(xù)的滿足條件的消息之后东羹,再從隊(duì)列后面繼續(xù)查詢是否有滿足條件的消息需要被移除剂桥。

3.3.6 Java層總結(jié)

最后用一張圖,來表示整個(gè)消息機(jī)制

Handler通過sendMessage()發(fā)送Message到MessageQueue隊(duì)列属提;Looper通過loop()权逗,不斷提取出達(dá)到觸發(fā)條件的Message,并將Message交給target(發(fā)送Message的Handler)來處理;經(jīng)過dispatchMessage()后斟薇,交回給Handler的handleMessage()來進(jìn)行相應(yīng)地處理火惊。

將Message加入MessageQueue時(shí),往管道寫入字符奔垦,可以喚醒loop線程屹耐;如果MessageQueue中沒有Message,并處于Idle狀態(tài)椿猎,則會(huì)執(zhí)行IdleHandler接口中的方法惶岭,往往用于做一些清理性地工作。

消息分發(fā)的優(yōu)先級(jí):Message的回調(diào)方法:message.callback.run()犯眠,優(yōu)先級(jí)最高按灶;Handler的回調(diào)方法:Handler.mCallback.handleMessage(msg),優(yōu)先級(jí)其次筐咧;Handler的默認(rèn)方法:Handler.handleMessage(msg)鸯旁,優(yōu)先級(jí)最低。

3.3.7 native消息機(jī)制

前面我們講解了Java層的消息處理機(jī)制量蕊,其中MessageQueue類里面涉及到多個(gè)native方法铺罢,除了MessageQueue的native方法,native層本身也有一套完整的消息機(jī)制残炮,用于處理native的消息韭赘。在整個(gè)消息機(jī)制中,而MessageQueue是連接Java層和Native層的紐帶势就,換言之泉瞻,Java層可以向MessageQueue消息隊(duì)列中添加消息,Native層也可以向MessageQueue消息隊(duì)列中添加消息苞冯。

MessageQueue

在MessageQueue中的native方法如下:

private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

關(guān)于這些方法不再進(jìn)行詳述袖牙,下面簡單提及其實(shí)現(xiàn)原理:
nativeInit()方法,最終實(shí)現(xiàn)由epoll機(jī)制中的epoll_create()/epoll_ctl()完成舅锄;
nativeDestroy()方法鞭达,最終實(shí)現(xiàn)由RefBase::decStrong()完成;
nativePollOnce()方法巧娱,最終實(shí)現(xiàn)由Looper::pollOnce()完成碉怔;
nativeWake()方法,最終實(shí)現(xiàn)由Looper::wake()調(diào)用write方法禁添,向管道寫入字符;
nativeIsPolling()桨踪,nativeSetFileDescriptorEvents()這兩個(gè)方法類似老翘,此處就不一一列舉。

總結(jié)

MessageQueue通過mPtr變量保存NativeMessageQueue對(duì)象,從而使得MessageQueue成為Java層和Native層的樞紐铺峭,既能處理上層消息墓怀,也能處理native層消息;下面列舉Java層與Native層的對(duì)應(yīng)圖

紅色虛線關(guān)系:Java層和Native層的MessageQueue通過JNI建立關(guān)聯(lián)卫键,彼此之間能相互調(diào)用傀履,搞明白這個(gè)互調(diào)關(guān)系,也就搞明白了Java如何調(diào)用C++代碼莉炉,C++代碼又是如何調(diào)用Java代碼钓账。
藍(lán)色虛線關(guān)系:Handler/Looper/Message這三大類Java層與Native層并沒有任何的真正關(guān)聯(lián)因妇,只是分別在Java層和Native層的handler消息模型中具有相似的功能膝擂。都是彼此獨(dú)立的奴曙,各自實(shí)現(xiàn)相應(yīng)的邏輯仰泻。
WeakMessageHandler繼承于MessageHandler類北专,NativeMessageQueue繼承于MessageQueue類

另外鳖悠,消息處理流程是先處理Native Message肺樟,再處理Native Request枝誊,最后處理Java Message窘游。理解了該流程唠椭,也就明白有時(shí)上層消息很少,但響應(yīng)時(shí)間卻較長的真正原因忍饰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泪蔫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子喘批,更是在濱河造成了極大的恐慌撩荣,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饶深,死亡現(xiàn)場離奇詭異餐曹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)敌厘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門台猴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俱两,你說我怎么就攤上這事饱狂。” “怎么了宪彩?”我有些...
    開封第一講書人閱讀 167,834評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵休讳,是天一觀的道長。 經(jīng)常有香客問我尿孔,道長俊柔,這世上最難降的妖魔是什么筹麸? 我笑而不...
    開封第一講書人閱讀 59,543評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮雏婶,結(jié)果婚禮上物赶,老公的妹妹穿的比我還像新娘。我一直安慰自己留晚,他們只是感情好酵紫,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著错维,像睡著了一般奖地。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上需五,一...
    開封第一講書人閱讀 52,196評(píng)論 1 308
  • 那天鹉动,我揣著相機(jī)與錄音,去河邊找鬼宏邮。 笑死泽示,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蜜氨。 我是一名探鬼主播械筛,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼飒炎!你這毒婦竟也來了埋哟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,671評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤郎汪,失蹤者是張志新(化名)和其女友劉穎赤赊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煞赢,經(jīng)...
    沈念sama閱讀 46,221評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抛计,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了照筑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吹截。...
    茶點(diǎn)故事閱讀 40,444評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凝危,靈堂內(nèi)的尸體忽然破棺而出波俄,到底是詐尸還是另有隱情,我是刑警寧澤蛾默,帶...
    沈念sama閱讀 36,134評(píng)論 5 350
  • 正文 年R本政府宣布懦铺,位于F島的核電站,受9級(jí)特大地震影響趴生,放射性物質(zhì)發(fā)生泄漏阀趴。R本人自食惡果不足惜昏翰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評(píng)論 3 333
  • 文/蒙蒙 一苍匆、第九天 我趴在偏房一處隱蔽的房頂上張望刘急。 院中可真熱鬧,春花似錦浸踩、人聲如沸叔汁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽据块。三九已至,卻和暖如春折剃,著一層夾襖步出監(jiān)牢的瞬間另假,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評(píng)論 1 272
  • 我被黑心中介騙來泰國打工怕犁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留边篮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,837評(píng)論 3 376
  • 正文 我出身青樓奏甫,卻偏偏與公主長得像戈轿,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阵子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容