1. 程式人生 > >Android訊息處理機制 -- native Handler -- Android Oreo

Android訊息處理機制 -- native Handler -- Android Oreo

為什麼使用handler?1、從UI的層面來看:螢幕的重新整理頻率是60HZ,約16ms 一次。為了保證UI的流暢性,不阻塞,需要耗時的操作非同步執行,然後通過handler非同步更新UI主執行緒。2、Android中的UI控制元件不是執行緒安全的,因此在多執行緒併發訪問UI的時候會導致UI控制元件處於不可預期的狀態。Google不通過鎖的機制來處理這個問題是因為:引入鎖會導致UI的操作變得複雜,導致UI的執行效率降低因此,Google的工程師最後是通過單執行緒的模型來操作UI,開發者只需要通過Handler在不同執行緒之間切花就可以了。概述Android的訊息機制?(java的實現機制)Android的訊息機制就是Handler的執行機制。主執行緒和子執行緒之間的切換是其中一種特殊運用,哪裡都有它的身影。瞭解Handler訊息傳遞機制,同時需要了解MessageQueue、Looper、Handler。
整個訊息機制的流程:建立訊息佇列-->進入訊息迴圈(有訊息則處理,沒有則進入睡眠等待狀態)-->傳送/處理訊息MessageQueue:描述訊息佇列。Looper:建立訊息佇列,進入訊息迴圈。Handler:傳送,處理訊息。訊息迴圈中,起關鍵作用的是一個pipe。在Looper物件建立的過程中,會在內部建立一個pipe。Looper中儲存pipe的讀端檔案描述符(read Fd)和寫端檔案描述符(write Fd)。當其他執行緒向這個執行緒的訊息佇列傳送訊息後,其他執行緒會通過這個pipe的read Fd寫入一個數據,從而喚醒執行緒,然後處理訊息。linux epoll機制可以監聽IO的讀寫事件。
為什麼在子執行緒中建立Handler會拋異常?Handler的工作是依賴於Looper的,而Looper(與訊息佇列)又是屬於某一個執行緒(ThreadLocal是執行緒內部的資料儲存類,通過它可以在指定執行緒中儲存資料,其他執行緒則無法獲取到),其他執行緒不能訪問。因此Handler就是間接跟執行緒是繫結在一起了。因此要使用Handler必須要保證Handler所建立的執行緒中有Looper物件並且啟動迴圈。因為子執行緒中預設是沒有Looper的,所以會報錯。主執行緒中預設是建立了Looper並且啟動了訊息的迴圈的,因此不會報錯:應用程式的入口是ActivityThread的main方法,在這個方法裡面會建立Looper,並且執行Looper的loop方法來啟動訊息的迴圈,使得應用程式一直執行。
子執行緒中可以通過Handler傳送訊息給主執行緒嗎?可以。有時候出於業務需要,主執行緒可以向子執行緒傳送訊息。子執行緒的Handler必須按照上述方法建立,並且關聯Looper。 android framework native Ahandler 機制 ?handler訊息機制,構成就必須包括一個Loop,message。那麼對應的AHandler,也應該有對應的ALooper, AMessage。AMessage:存放message的一個類,呼叫一系列的函式來進行實現。ALooper:用於訊息佇列的建立和迴圈。AHandler:AHandler 是一個基類, 所有想使用以上這套訊息機制的實現都必須繼承它。以Android O - NuPlayer為例:1、NuPlayerDriver是mediaplayer的起始,建立Looper 和對應的Handler,進入訊息迴圈狀態建構函式中:NuPlayerDriver::NuPlayerDriver(pid_t pid);
NuPlayerDriver::NuPlayerDriver(pid_t pid)
    :   mLooper(new ALooper),   -- create a looper
        mPlayer(new NuPlayer(pid)) --mPlayer這裡是NuPlayer,繼承自AHandler,相當於AHandler。
      ……
      {
    ALOGD("NuPlayerDriver(%p) created, clientPid(%d)", this, pid);
    mLooper->setName("NuPlayerDriver Looper"); -- 給該looper取個名稱,便於與AHandler對應

    ……
    mLooper->start(
            false, /* runOnCallingThread */
            true,  /* canCallJava */
            PRIORITY_AUDIO);    --- 啟動 looper

    mLooper->registerHandler(mPlayer); -- 將AHandler (handid)註冊到Looper中。

    mPlayer->setDriver(this);
}
mLooper->start();
status_t ALooper::start(
        bool runOnCallingThread, bool canCallJava, int32_t priority) {
      ……
     mThread = new LooperThread(this, canCallJava);  -- 新建looper執行緒

    status_t err = mThread->run(
            mName.empty() ? "ALooper" : mName.c_str(), priority);  -- 啟動looper 執行緒
    if (err != OK) {
        mThread.clear();
    }

    return err;
}
mLooper->registerHandler(mPlayer);
ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
    return gLooperRoster.registerHandler(this, handler);
}

ALooper::handler_id ALooperRoster::registerHandler(    -- 將ALooper 和AHandler註冊到ALooperRoster
        const sp<ALooper> &looper, const sp<AHandler> &handler) {
    Mutex::Autolock autoLock(mLock);

    if (handler->id() != 0) {
        CHECK(!"A handler must only be registered once.");
        return INVALID_OPERATION;
    }

    HandlerInfo info;
    info.mLooper = looper; -- 對應“NuPlayerDriver Looper” looper
    info.mHandler = handler;  -- 對應“NuPlayer”
    ALooper::handler_id handlerID = mNextHandlerID++;
    mHandlers.add(handlerID, info); -- 將該looper,hander資訊新增進keyedVector。

    handler->setID(handlerID, looper); -- 設定handlerID,便於發message時找到對應的handler。

    return handlerID;
}
ALooperRoster::ALooperRoster()
    : mNextHandlerID(1) {   -- 從1開始
}
ALooperRoster gLooperRoster;  --全域性變數,全域性變數gLooperRoster(管理很多ALooper)會根據傳入的msg, 找出msg 中handle id, 根據這個id 找到對應的looper, 從而將訊息送到具體的looper 來處理。
AHandler類的成員
struct AHandler : public RefBase {
    AHandler()
        : mID(0),
          mVerboseStats(false),
          mMessageCounter(0) {
    }

    ALooper::handler_id id() const {
        return mID;
    }

    sp<ALooper> looper() const {
        return mLooper.promote();
    }

    wp<ALooper> getLooper() const {
        return mLooper;
    }

    wp<AHandler> getHandler() const {
        // allow getting a weak reference to a const handler
        return const_cast<AHandler *>(this);
    }

protected:
    virtual void onMessageReceived(const sp<AMessage> &msg) = 0; --處理訊息函式

    ……
};
2、傳遞message資訊。先建立一個AMessage,傳入HandID,然後post
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);  -- 建立一個kWhatSetDataSource
的message例項,傳入的引數為事件的名稱和處理該訊息的HandID。
    sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);

    sp<GenericSource> source =
            new GenericSource(notify, mUIDValid, mUID);

    ALOGV("setDataSourceAsync fd %d/%lld/%lld source: %p",
            fd, (long long)offset, (long long)length, source.get());

    status_t err = source->setDataSource(fd, offset, length);

    if (err != OK) {
        ALOGE("Failed to set data source!");
        source = NULL;
    }

    msg->setObject("source", source); --設定source的obj,後續可根據source,選擇對應的obj。
    msg->post();
    mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
}
AMessage建立過程:
AMessage::AMessage(uint32_t what, const sp<const AHandler> &handler)
    : mWhat(what),
      mNumItems(0) {
    setTarget(handler);
}
void AMessage::setTarget(const sp<const AHandler> &handler) {
    if (handler == NULL) {
        mTarget = 0;
        mHandler.clear();
        mLooper.clear();
    } else {
        mTarget = handler->id();    -- 儲存handler id
        mHandler = handler->getHandler();
        mLooper = handler->getLooper();
    }
}
void AMessage::setObjectInternal(
        const char *name, const sp<RefBase> &obj, Type type) {
    Item *item = allocateItem(name);
    item->mType = type;

    if (obj != NULL) { obj->incStrong(this); }
    item->u.refValue = obj.get();
}

void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
    setObjectInternal(name, obj, kTypeObject);
}
AMessage Post過程:
status_t AMessage::post(int64_t delayUs) {
    sp<ALooper> looper = mLooper.promote(); --promote() 是wp的函式,將wp物件,返回sp物件。
    if (looper == NULL) {
        ALOGW("failed to post message as target looper for handler %d is gone.", mTarget);
        return -ENOENT;
    }

    looper->post(this, delayUs); -- 往“NuPlayerDriver Looper”傳遞訊息
    return OK;
}
void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
    Mutex::Autolock autoLock(mLock);
   ……
    List<Event>::iterator it = mEventQueue.begin();
    while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
        ++it;
    }

    Event event;
    event.mWhenUs = whenUs;
    event.mMessage = msg;

    if (it == mEventQueue.begin()) {
        mQueueChangedCondition.signal();
    }

    mEventQueue.insert(it, event); --往訊息佇列裡填充訊息
}
AMessage 處理過程:當訊息佇列裡有訊息了,Looper執行緒能監測到。當佇列裡有訊息時便會觸發loop函式
bool ALooper::loop() {
    Event event;
    ……
    event.mMessage->deliver();
     ……
}
void AMessage::deliver() {
    sp<AHandler> handler = mHandler.promote();
    if (handler == NULL) {
        ALOGW("failed to deliver message as target handler %d is gone.", mTarget);
        return;
    }

    handler->deliverMessage(this);
}
void AHandler::deliverMessage(const sp<AMessage> &msg) {
    onMessageReceived(msg);
    mMessageCounter++;

    ……
}
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatSetDataSource:
        {
            ALOGV("kWhatSetDataSource");

            CHECK(mSource == NULL);

            status_t err = OK;
            sp<RefBase> obj;
            CHECK(msg->findObject("source", &obj));
            if (obj != NULL) {
                Mutex::Autolock autoLock(mSourceLock);
                mSource = static_cast<Source *>(obj.get());  -- 指向GeneriousSource
            } else {
                err = UNKNOWN_ERROR;
            }
            ……
            break;
        }
}
總結 -- Handler的處理flow:1、create ALooper/AHandler2、啟動Looper。-- 處於訊息佇列迴圈狀態3、註冊AHandler 。-- AHandler依賴於ALooper,共同註冊到ALooperRoster 型別的全域性變數(管理N多個AHandler和ALooper組合)4、create AMessage5、傳送message。-- 填充messag 到訊息佇列

6、處理message。-- 當訊息佇列裡有訊息了,Looper執行緒能監測到。當佇列裡有訊息時便會觸發loop函式,交給對應的Handler去處理。