Android訊息處理機制 -- native Handler -- Android Oreo
阿新 • • 發佈:2019-02-09
為什麼使用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,然後postvoid 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去處理。