線程訊息通訊與非同步處理,線程訊息非同步

來源:互聯網
上載者:User

線程訊息通訊與非同步處理,線程訊息非同步
轉載請標明出處: 

http://blog.csdn.net/yujun411522/article/details/46444869
本文出自:【yujun411522的部落格】


關於android內訊息通訊和handler的知識在之前的Handler中已經簡要介紹過了,這裡介紹在native層的實現機制。相信大家都知道一個標準的Looper線程的寫法:
public MyLooperThread extends Thread{     private Handler mHandler;     public void run(){     //在執行個體化handler之前一定要先調用Looper.prepare()方法     //1 Looper.prepare()方法     Looper.prepare();     //2 執行個體化handler     //mHandler = new Handler(){               public void handleMessage(Message msg){                         //處理msg操作                    }          };     //3 Looper.loop()不斷從訊息佇列中取出來資訊     Looper.loop();     }     }
典型的三個操作:1 調用Looper.prepare方法,進入準備階段。2 執行個體化Handler對象。3 調用Looper.loop方法,進入迴圈。注意,三者的順序不能有錯,下面會介紹 
7.1 Looper.prepare進入準備階段先看Looper類中prepare方法:
public class Looper {   // sThreadLocal.get() will return null unless you've called prepare().    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();    final MessageQueue mQueue;//內部維護一個訊息佇列    final Thread mThread;//對應的線程    volatile boolean mRun;//運行狀態    private static Looper mMainLooper = null;  // guarded by Looper.class,主線程的Looper   public static void prepare() {        if (sThreadLocal.get() != null) {//之前已經設定過Looper對象了,報異常,說明只能設定一次            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());//為sThreadLocal變數設定Looper,這個Looper必須有,且只能有一個,不能重複設定    }}
sThreadLocal 是一個用來儲存當前線程狀態的一個成員變數,儲存範圍為線程內部。在調用prepare方法時,如果之前線程設定Looper對象就報異常,如果沒有設定Looper就為sThreadLocal設定一個new Looper()對象,再看Looper的建構函式: 
private Looper() {//私人方法,不允許外部存取它        mQueue = new MessageQueue();//構造一個MessageQueue對象,Looper中維護了一個訊息佇列,這一點很重要        mRun = true;//設定mRun為true        mThread = Thread.currentThread();//mThread設定為當前線程    }
其中重要的就是MessageQueue訊息佇列的建立,看它的構造方法:
 MessageQueue() {        nativeInit();//這是一個本地方法    }
nativeInit是一個本地方法,對應的實現方法在android_os_MessageQueue.cpp檔案中   先看NativeMessageQueue類:
class NativeMessageQueue {public:    NativeMessageQueue();    ~NativeMessageQueue();    inline sp<Looper> getLooper() { return mLooper; }//返回該Looper變數,這個Looper類是native中的Looper類,不是java層的    void pollOnce(int timeoutMillis);//後面會分析,進行詢問    void wake();//後面會分析,向管道寫入"W"private:    sp<Looper> mLooper;//維護一個Native層的Looper變數};
再來看MessageQueue中nativeInit方法中的的實現android_os_MessageQueue_nativeInit函數:
static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {     //obj為java層的MessageQueue對象     //1 建立一個NativeMessageQueue對象,這個是native層的    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();    if (! nativeMessageQueue) {        jniThrowRuntimeException(env, "Unable to allocate native queue");        return;    }    //2 將NativeMessageQueue和 java層的MessageQueue對象 關聯起來,實際就是講nativeMessagQueue對象儲存到MessageQueue中的mPtr變數中    android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);}
兩個主要工作: 1 建立NativeMessageQueue對象 ;2 將java層的MessageQueue對象和NativeMessageQueue關聯起來
7.1.1 建立NativeMessageQueue對象看NativeMessageQueue的建構函式:
NativeMessageQueue::NativeMessageQueue() {    mLooper = Looper::getForThread();//調用getForThread,看傳回值是否為null    if (mLooper == NULL) {//如果傳回值為null,則建立一個Looper並設定        mLooper = new Looper(false);        Looper::setForThread(mLooper);//    }}
先看Looper::getForThread函數是否為null,如果為null,構造一個Looper對象,並調用setForThread設定為線程唯一的一個Looper,這一部分工作和java層中的Looper.prepare()方法相同。那麼再看一下native層的Looper對象是如何執行個體化的,看它的建構函式:
Looper::Looper(bool allowNonCallbacks) :        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),        mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {    int wakeFds[2];    //建立一個管道,讀寫端分別為wakeFds[0]和wakeFds[1]     int result = pipe(wakeFds);     mWakeReadPipeFd = wakeFds[0];//讀端    mWakeWritePipeFd = wakeFds[1];//寫端    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);//設定非阻塞方式    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);//設定非阻塞方式      mEpollFd = epoll_create(EPOLL_SIZE_HINT);//監聽管道    struct epoll_event eventItem;    memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union    eventItem.events = EPOLLIN;    eventItem.data.fd = mWakeReadPipeFd;    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);//只監聽mWakeReadPipeFd ,也就是讀端    }

7.1.2 將java層的MessageQueue對象和NativeMessageQueue關聯起來關聯工作由android_os_MessageQueue_setNativeMessageQueue函數來完成:
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj,        NativeMessageQueue* nativeMessageQueue) {    env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr,             reinterpret_cast<jint>(nativeMessageQueue));}
實際就是將nativeMessageQueue對象的地址複製給java層MessageQueue中的gMessageQueueClassInfo.mPtr變數,那麼gMessageQueueClassInfo中mPtr是什麼:
static struct {    jfieldID mPtr;   // native object attached to the DVM MessageQueue} gMessageQueueClassInfo;
它的執行個體化在register_android_os_MessageQueue函數中:
int register_android_os_MessageQueue(JNIEnv* env) {    int res = jniRegisterNativeMethods(env, "android/os/MessageQueue",            gMessageQueueMethods, NELEM(gMessageQueueMethods));       jclass clazz;    FIND_CLASS(clazz, "android/os/MessageQueue");    //mPtre指向Java層中MessageQueue的mPtr成員變數    GET_FIELD_ID(gMessageQueueClassInfo.mPtr, clazz,"mPtr", "I");        return 0;}
綁定的結果就是在java層中的MessageQueue的mPtr儲存了NativeMessageQueue變數的地址,這樣就可以通過MessageQueue訪問NativeMessageQueue了,到此為止java層MessageQueue構造完成。可以看出Java層Looper.prepare()函數的作用就是在Looper中持有一個MessageQueue和當前線程的引用,而MessageQueue中的mPtr指向native層的NativeMessageQueue對象,NativeMessageQueue對象中又儲存了線程唯一的一個native Looper變數,四者之間的關係:
7.2 Handler的建立和發送訊息Looper是用來驅動訊息的,而Looper的訊息從哪裡來, Handler的發送;Looper的訊息哪裡去, Handler的處理。所以Handler的作用就是發送和處理訊息.
7.2.1 Handler的建立看沒有參數的構造方法: 
 public Handler() {         ....        //返回當前線程的Looper對象        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;//將Looper中的mQueue賦值給Handler的成員變數mQueue        mCallback = null;    }
它的構造中有一點注意,先通過Looper.myLooper方法擷取當前線程的Looper對象,如果為null報異常,所以在建立建立Handler之前一定要先調用Looper.prepare方法。
7.2.2 建立訊息這一部分參看Handler機制
7.2.3 訊息發送1 java層無論是通過post還是send都最終通過sendMessageAtTime函數來實現
 public boolean sendMessageAtTime(Message msg, long uptimeMillis)//uptimeMillis 絕對時間,開機時間作為基準    {        boolean sent = false;        MessageQueue queue = mQueue;//looper中建立的MessageQueue        if (queue != null) {            msg.target = this;//指明msg的處理者為this            sent = queue.enqueueMessage(msg, uptimeMillis);//調用MessageQueue  的enqueueMessage 方法        }        else {          .....        }        return sent;    }
調用MessageQueue  的enqueueMessage 方法   
 final boolean enqueueMessage(Message msg, long when) {        ....        final boolean needWake;        synchronized (this) {            if (mQuiting) {              return false;//handler所線上程正在退出            } else if (msg.target == null) {                mQuiting = true;            }            msg.when = when;//指明訊息何時處理            //mMessages 訊息佇列頭部,p為當前訊息            Message p = mMessages;             // 訊息佇列為空白或者新訊息需要立馬處理或者新訊息處理時間早於隊首訊息的處理時間            if (p == null || when == 0 || when < p.when) {//如果是這些情況,需要將新訊息插入到隊首立馬處理                msg.next = p;                mMessages = msg;                needWake = mBlocked; // new head, might need to wake up            } else {//否則找到合適的位置插入,按照時間的順序                Message prev = null;                while (p != null && p.when <= when) {                    prev = p;                    p = p.next;                }                msg.next = prev.next;                prev.next = msg;                needWake = false; // still waiting on head, no need to wake up            }        }        if (needWake) {//新加入訊息佇列,且處於block狀態,需要喚醒            nativeWake(mPtr);//本地方法,mPtr 就是NativeMessageQueue的native地址        }        return true;    }
新加入訊息佇列,且處於block狀態,需要喚醒,調用本地方法nativeWake,mPtr 就是NativeMessageQueue的native地址,對應的native實現在android_os_MessageQueue.cpp檔案中的android_os_MessageQueue_nativeWake方法:
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) {     //ptr 就是NativeMessageQueue的地址    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);    return nativeMessageQueue->wake();}
繼續調用nativeMessageQueue的wake方法:
void NativeMessageQueue::wake() {    mLooper->wake();}
調用mLooper的wake方法,在Looper.cpp檔案中:
void Looper::wake() {    if (mPendingWakeCount++ == 0) {        mPendingWakeTime = systemTime(SYSTEM_TIME_MONOTONIC);    }    ssize_t nWrite;    do {        nWrite = write(mWakeWritePipeFd, "W", 1);//向mWakeWritePipeFd 寫入一個"W"    } while (nWrite == -1 && errno == EINTR);    if (nWrite != 1) {        if (errno != EAGAIN) {            LOGW("Could not write wake signal, errno=%d", errno);        }    }}
還記得native層建立的Looper對象時建立的管道嗎?mWakeWritePipeFd 就是管道的寫端,一旦寫入了"W"字元,處理訊息的線程就會被I/O喚醒。
2 native層native層可以發送訊息,但不是像java層中通過handler來發送,而是通過native層的Looper對象來發送,最終都是通過sendMessageAtTime方法來實現,在Looper.cpp檔案中:
void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,        const Message& message) {     size_t i = 0;    {         AutoMutex _l(mLock);         //native層訊息佇列的大小        size_t messageCount = mMessageEnvelopes.size();         //按照時間先後順序找到它插入的位置         while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {            i += 1;        }        //利用MessageEnvelope  ,將發送時間,messageHandler和訊息封裝在MessageEnvelope  中        MessageEnvelope messageEnvelope(uptime, handler, message);        //插入到native訊息佇列中        mMessageEnvelopes.insertAt(messageEnvelope, i, 1);        if (mSendingMessage) {            return;        }    }           if (i == 0) {        wake();//和java層調用nativeWake 方法效果一樣,向管道寫端寫入一個"W"    }}
native層發送訊息的過程是先找到插入的位置,然後將message,handler,發送時間等資訊封裝到MessageEnvelope中,最後插入到訊息佇列的合適位置之中。其中涉及到一個結構體:MessageEnvelope: 
struct MessageEnvelope {        MessageEnvelope() : uptime(0) { }        MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler,                const Message& message) : uptime(uptime), handler(handler), message(message) {        }        nsecs_t uptime; //執行事件        sp<MessageHandler> handler;//執行handler        Message message;//訊息    };
到目前為止,訊息已經發送成功了,接下來就是取出訊息並處理。  7.3 Looper.loop 迴圈處理訊息看Looper.looper方法的實現:     
  public static void loop() {        Looper me = myLooper();        if (me == null) {//可以看出,必須在設定當前線程的Looper之後才能執行loop方法            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        MessageQueue queue = me.mQueue;               .....               while (true) {            //1 從MessageQueue中取出訊息            Message msg = queue.next(); // might block            if (msg != null) {                if (msg.target == null) {                 return;//沒有訊息時退出                }                .....                //派發訊息                msg.target.dispatchMessage(msg);                ....               //回收訊息                msg.recycle();            }        }    }
重要的操作就兩個,最重要的第一步從messageQuene中取出訊息;2 派發該訊息處理函數 
7.3.1 messageQuene.next取出合適的訊息   
final Message next() {              int nextPollTimeoutMillis = 0;//下一次poll的時間,0表示馬上進行        for (;;) {             ...            //本地方法nativePollOnce            nativePollOnce(mPtr, nextPollTimeoutMillis);            synchronized (this) {//同步                              final long now = SystemClock.uptimeMillis();                final Message msg = mMessages;//隊首訊息                if (msg != null) {                    final long when = msg.when;//隊首訊息的執行事件 ,這裡假如是4s,now為5s                    if (now >= when) {//說明隊首訊息已經到了或者過了執行事件,所以立馬執行                        mBlocked = false;                        mMessages = msg.next;//取出隊首訊息,返回                        msg.next = null;                                                msg.markInUse();//標記正在使用                        return msg;                    } else {                        //否則,假如隊首訊息執行時間when為10s,now為5s,則過when-now= 5s之後再詢問                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);                    }                } else {                    //訊息佇列中沒有訊息                    nextPollTimeoutMillis = -1;                }                             }                      }    }
這裡的關鍵區段是nativePollOnce本地方法,它的實現在android_os_MessageQueue.cpp中:
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,        jint ptr, jint timeoutMillis) {    //其中ptr就是NativeMessageQueue對象地址,timeOutMills就是經過多長時間詢問    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);    nativeMessageQueue->pollOnce(timeoutMillis);}
可以看出調用NativeMessageQueue的pollOnce方法
void NativeMessageQueue::pollOnce(int timeoutMillis) {    mLooper->pollOnce(timeoutMillis);}
又調用Looper的pollOnce方法:
 inline int pollOnce(int timeoutMillis) {        return pollOnce(timeoutMillis, NULL, NULL, NULL);    }
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {     //timeoutMillis, NULL, NULL, NULL 參數    int result = 0;    for (;;) {        while (mResponseIndex < mResponses.size()) {            const Response& response = mResponses.itemAt(mResponseIndex++);            ALooper_callbackFunc callback = response.request.callback;            if (!callback) {                int ident = response.request.ident;                int fd = response.request.fd;                int events = response.events;                void* data = response.request.data;                  if (outFd != NULL) *outFd = fd;                if (outEvents != NULL) *outEvents = events;                if (outData != NULL) *outData = data;                return ident;            }        }        if (result != 0) {             if (outFd != NULL) *outFd = 0;            if (outEvents != NULL) *outEvents = NULL;            if (outData != NULL) *outData = NULL;            return result;        }        //執行pollInner方法        result = pollInner(timeoutMillis);    }}
進一步調用了pollInner方法:
int Looper::pollInner(int timeoutMillis) {     // Adjust the timeout based on when the next message is due.    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);        if (messageTimeoutMillis >= 0                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {            timeoutMillis = messageTimeoutMillis;        }     int result = ALOOPER_POLL_WAKE;    mResponses.clear();    mResponseIndex = 0;     // Wait for wakeAndLock() waiters to run then set mPolling to true.    mLock.lock();    while (mWaiters != 0) {        mResume.wait(mLock);    }    mPolling = true;    mLock.unlock();    size_t requestedCount = mRequestedFds.size();    int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);//監控mRequestedFds 上的時間      for (int i = 0; i < eventCount; i++) {        int fd = eventItems[i].data.fd;        uint32_t epollEvents = eventItems[i].events;        if (fd == mWakeReadPipeFd) {            if (epollEvents & EPOLLIN) {                awoken();//調用awoken方法            } else {                  ....            }        } else {          .....        }    }Done: ;     ...    return result;}
這裡調用的awoken方法:
void Looper::awoken() {    char buffer[16];    ssize_t nRead;    do {         //讀管道寫端寫入的"W"        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));}

7.3.2 msg.target.dispatchMessage(msg)分發訊息這部分參看Handler機制的實現

  7.4 AsyncTask這部分參看AsyncTask源碼分析










聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.