深入理解Android消息机制Word文档格式.docx

上传人:b****6 文档编号:17191352 上传时间:2022-11-28 格式:DOCX 页数:16 大小:35.44KB
下载 相关 举报
深入理解Android消息机制Word文档格式.docx_第1页
第1页 / 共16页
深入理解Android消息机制Word文档格式.docx_第2页
第2页 / 共16页
深入理解Android消息机制Word文档格式.docx_第3页
第3页 / 共16页
深入理解Android消息机制Word文档格式.docx_第4页
第4页 / 共16页
深入理解Android消息机制Word文档格式.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

深入理解Android消息机制Word文档格式.docx

《深入理解Android消息机制Word文档格式.docx》由会员分享,可在线阅读,更多相关《深入理解Android消息机制Word文档格式.docx(16页珍藏版)》请在冰豆网上搜索。

深入理解Android消息机制Word文档格式.docx

7.} 

这里可以设置Message为异步消息 

查看queue的enqueueMessage方法,我们剥离出核心代码:

1.if 

(p 

== 

null 

|| 

when 

p.when) 

// 

New 

head, 

wake 

up 

the 

event 

queue 

blocked. 

msg.next 

p;

mMessages 

msg;

needWake 

mBlocked;

如果是新的队列头,直接插入队列 

如果队列里面已经有消息了,执行如下逻辑 

1.needWake 

mBlocked 

&

p.target 

msg.isAsynchronous();

2.Message 

prev;

3.for 

(;

;

) 

prev 

p.next;

7. 

break;

8. 

9. 

(needWake 

p.isAsynchronous()) 

10. 

false;

11. 

12.} 

13.msg.next 

invariant:

prev.next 

14.prev.next 

插入消息的时候,一般不会唤醒消息队列。

如果消息是异步的,并且队列头不是一个异步消息的时候,会唤醒消息队列 

(needWake) 

nativeWake(mPtr);

3.} 

消息队列的具体唤醒过程我们暂时不细看。

把关注点移到Looper上。

looper在执行的时候具体执行了什么逻辑呢?

查看Looper.java的looper()方法 

looper方法中有一个死循环,在死循环中,会获取下一个Message 

1.for 

msg 

queue.next();

might 

block 

(msg 

!

null) 

2.// 

Stalled 

by 

barrier. 

Find 

next 

asynchronous 

message 

in 

queue. 

3.do 

prevMsg 

msg.next;

while 

msg.isAsynchronous());

当存在一个barrier消息的时候,会寻找队列中下一个异步任务。

而不是按照顺序。

例如3个消息,1,2,3,2是异步消息。

如果不存在barrier的时候,next的顺序就是1,2,3但是如果存在barrier的时候,则是2,1,3 

(now 

msg.when) 

Next 

is 

not 

ready. 

Set 

timeout 

to 

it 

nextPollTimeoutMillis 

(int) 

Math.min(msg.when 

now, 

Integer.MAX_VALUE);

else 

Got 

message. 

(prevMsg 

prevMsg.next 

12. 

13. 

null;

14. 

(DEBUG) 

Log.v(TAG, 

"

Returning 

message:

msg);

15. 

msg.markInUse();

16. 

17. 

18.} 

19. 

No 

more 

messages. 

20. 

-1;

21.} 

这里如果next的Message不为空,就返回,并且将它移出队列在MessageQueue为空的时候,会顺便去处理一下add过的IdleHandler,处理一些不重要的消息 

(int 

pendingIdleHandlerCount;

i++) 

IdleHandler 

idler 

mPendingIdleHandlers[i];

mPendingIdleHandlers[i] 

release 

reference 

handler 

keep 

try 

idler.queueIdle();

catch 

(Throwable 

t) 

Log.wtf(TAG, 

threw 

exception"

 

t);

(!

keep) 

synchronized 

(this) 

mIdleHandlers.remove(idler);

16.} 

查看IdleHandler的源码。

1.* 

Callback 

interface 

for 

discovering 

thread 

going 

waiting 

*/ 

public 

static 

/** 

Called 

has 

run 

out 

of 

messages 

and 

will 

now 

wait 

more. 

Return 

true 

your 

idle 

active, 

false 

have 

removed. 

This 

may 

be 

called 

there 

are 

still 

pending 

but 

they 

all 

scheduled 

dispatched 

after 

current 

time. 

queueIdle();

当queueIdle()为false的时候,会将它从mIdleHandlers中remove,仔细思考下,我们其实可以利用IdleHandler实现不少功能,例如 

1.Looper.myQueue().addIdleHandler(new 

MessageQueue.IdleHandler() 

@Override 

queueIdle() 

6.});

我们可以在queueIdle中,趁着没有消息要处理,统计一下页面的渲染时间(消息发送完了说明UI已经渲染完了),或者算一下屏幕是否长时间没操作等等。

拿到Message对象后,会将Message分发到对应的target去 

1.msg.target.dispatchMessage(msg);

查看源码 

void 

dispatchMessage(Message 

msg) 

(msg.callback 

handleCallback(msg);

(mCallback 

(mCallback.handleMessage(msg)) 

return;

handleMessage(msg);

当msg的callback不为null的时候,即通过post(Runnable)发送信息的会执行handlerCallback(msg)方法。

如果mCallback不为null并且handleMessage的结果为false,则执行handleMessage方法。

否则会停止分发。

handleCallback(Message 

message) 

message.callback.run();

查看handlerCallback方法源码,callback会得到执行。

到这里基本的Android消息机制就分析完了,简而言之就是,Handler不断的将Message发送到一根据时间进行排序的优先队列里面,而线程中的Looper则不停的从MQ里面取出消息,分发到相应的目标Handler执行。

为什么主线程不卡?

分析完基本的消息机制,既然Looper的looper方法是一个for(;

)循环,那么新的问题提出来了。

为什么Android会在主线程使用死循环?

执行死循环的时候为什么主线程的阻塞没有导致CPU占用的暴增?

� 

继续分析在源码中我们没有分析的部分:

∙消息队列构造的时候是否调用了jni部分

∙nativeWake、nativePollOnce这些方法的作用是什么

先查看MQ的构造方法:

1.MessageQueue(boolean 

quitAllowed) 

mQuitAllowed 

quitAllowed;

mPtr 

nativeInit();

4.} 

会发现消息队列还是和native层有关系,继续查看android/platform/frameworks/base/core/jni/android_os_MessageQueue_nativeInit.cpp中nativeInit的实现:

1.static 

jlong 

android_os_MessageQueue_nativeInit(JNIEnv* 

env, 

jclass 

clazz) 

NativeMessageQueue* 

nativeMessageQueue 

new 

NativeMessageQueue();

nativeMessageQueue) 

jniThrowRuntimeException(env, 

Unable 

allocate 

native 

queue"

);

nativeMessageQueue->

incStrong(env);

reinterpret_cast<

jlong>

(nativeMessageQueue);

10.} 

这里会发现我们初始化了一个NativeMessageQueue,查看这个消息队列的构造函数 

1.NativeMessageQueue:

:

NativeMessageQueue() 

mPollEnv(NULL), 

mPollObj(NULL), 

mExceptionObj(NULL) 

mLooper 

Looper:

getForThread();

(mLooper 

NULL) 

Looper(false);

setForThread(mLooper);

8.} 

这里会发现在mq中初始化了native的Looper对象,查看android/platform/framework/native/libs/utils/Looper.cpp中Looper对象的构造函数 

1.// 

简化后的代码 

2.Looper:

Looper(bool 

allowNonCallbacks) 

mAllowNonCallbacks(allowNonCallbacks), 

mSendingMessage(false), 

mResponseIndex(0), 

mNextMessageUptime(LLONG_MAX) 

int 

wakeFds[2];

result 

pipe(wakeFds);

mWakeReadPipeFd 

wakeFds[0];

mWakeWritePipeFd 

wakeFds[1];

fcntl(mWakeReadPipeFd, 

F_SETFL, 

O_NONBLOCK);

fcntl(mWakeWritePipeFd, 

mEpollFd 

epoll_create(EPOLL_SIZE_HINT);

struct 

epoll_event 

eventItem;

18. 

memset(&

eventItem, 

0, 

sizeof(epoll_event));

eventItem.events 

EPOLLIN;

eventItem.data.fd 

mWakeReadPipeFd;

21. 

epoll_ctl(mEpollFd, 

EPOLL_CTL_ADD, 

mWakeReadPipeFd, 

eventItem);

22.} 

这里我们会发现,在native层创建了一个epoll,并且对epoll的event事件进行了监听。

什么是epoll 

在继续分析源码之前,我们先分析一下,什么是epoll 

epoll是Linux中的一种IO多路复用方式,也叫做event-driver-IO。

Linux的select多路复用IO通过一个select()调用来监视文件描述符的数组,然后轮询这个数组。

如果有IO事件,就进行处理。

select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。

epoll在select的基础上(实际是在poll的基础上)做了改进,epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可。

另一个本质的改进在于epoll采用基于事件的就绪通知方式(设置回调)。

在select中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知 

关于epoll和select,可以举一个例子来表达意思。

select的情况和班长告诉全班同学交作业类似,会挨个去询问作业是否完成,如果没有完成,班长会继续询问。

而epoll的情况则是班长询问的时候只是统计了待交作业的人数,然后告诉同学作业完成的时候告诉把作业放在某处,然后喊一下他。

然后班长每次都去这个地方收作业。

大致了解了epoll之后,我们继续查看nativePollOnce方法,同理,会调用nativeLooper的pollOnce方法 

1.while 

(mResponseIndex 

mResponses.size()) 

const 

Response&

response 

mResponses.itemAt(mResponseIndex++);

ident 

response.request.ident;

(ident 

>

fd 

response.request.fd;

events 

response.events;

void* 

data 

response.request.data;

(outFd 

*outFd 

fd;

(outEvents 

*outEvents 

events;

(outData 

*outData 

data;

ident;

在pollOnce中,会先处理没有callback的response(ALOOPER_POLL_CALLBACK=-2),处理完后会执行pollInner方法 

移除了部分细节处理和日志代码 

添加了分析源码的日志 

3.int 

pollInner(int 

timeoutMillis) 

(timeoutMillis 

mNextMessageUptime 

LLONG_MAX) 

nsecs_t 

systemTime(SYSTEM_TIME_MONOTONIC);

messageTimeoutMillis 

toMillisecondTimeoutDelay(now, 

mNextMessageUptime);

(messageTimeoutMillis 

timeoutMillis)) 

timeoutMillis 

messageTimeoutMillis;

Poll. 

ALOOPER_POLL_WAKE;

mResponses.clear();

16.

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 成人教育 > 成考

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1