Android中的消息机制Word下载.docx
- 文档编号:20476440
- 上传时间:2023-01-23
- 格式:DOCX
- 页数:12
- 大小:43.82KB
Android中的消息机制Word下载.docx
《Android中的消息机制Word下载.docx》由会员分享,可在线阅读,更多相关《Android中的消息机制Word下载.docx(12页珍藏版)》请在冰豆网上搜索。
(klass.getModifiers()&
Modifier.STATIC)==0){
Log.w(TAG,"
ThefollowingHandlerclassshouldbestaticorleaksmightoccur:
"
+
klass.getCanonicalName());
}
mLooper=Looper.myLooper();
if(mLooper==null){
thrownewRuntimeException(
Can'
tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()"
);
mQueue=mLooper.mQueue;
mCallback=callback;
mAsynchronous=async;
可以看到Looper调用myLooper方法获取到Looper对象,如果mLooper==null的话,会抛出
tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()
的异常。
大概的意思就是无法在没有调用Looper.prepare()的线程中创建handler。
我在刚开始学习Handler的时候经常会遇到这个错误。
不急,等下在分析到底为什么,现在我们只需要知道如果Looper.myLooper()没有获取到Looper对象的话就会报这个错。
到了这里,Handler和Looper就建立起了关联。
接着往下看完最后几行代码
mQueue=mLooper.mQueue;
从Looper对象中取出MessageQueue对象并赋值。
MessageQueue就是消息队列,那么他里面存储着很多消息吗?
到了这一步,Handler通过Looper与MessageQueue也建立起了关联。
我们跟踪Looper的myLooper方法进去,解决为什么会抛出Can’tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()异常。
myLooper方法源码如下:
publicstatic@NullableLoopermyLooper(){
returnsThreadLocal.get();
只有一行代码,从线程中取出Looper对象,那么我们有理由相信,这个ThreadLocal是通过set方法把Looper对象设置进去的。
想一想ThreadLocal在哪里把Looper对象设置进去了呢。
回到刚才想要解决的问题:
Can’tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()。
那会不会是Looper的prepare方法呢?
publicstaticvoidprepare(){
prepare(true);
prepare方法调用了它的一个参数的重载,那么我们就看看那个重载的方法
privatestaticvoidprepare(booleanquitAllowed){
if(sThreadLocal.get()!
=null){
thrownewRuntimeException("
OnlyoneLoopermaybecreatedperthread"
sThreadLocal.set(newLooper(quitAllowed));
找到了线索,ThreadLocal确实是在Looper的prepare方法里把Looper对象设置进去的,而且从第一行的判断可以知道,一个线程只有一个Looper对象。
到了这里,Looper与ThreadLocal建立起了关联。
可以看下Looper的构造方法
privateLooper(booleanquitAllowed){
mQueue=newMessageQueue(quitAllowed);
mThread=Thread.currentThread();
创建了一个MessageQueue对象。
好,结合我们的分析可以知道,如果Looper没有调用prepare方法,ThreadLocal的get方法就会返回空,那么Looper.myLooper()也会返回空,所以就抛出了Can'
tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()的异常。
那么问题又来了,我们写程序时好像没有手动调用Looper.prepare()吧,也不会抛出异常。
前面提到,我们通常都是在主线程,也就是UI线程中创建handler的。
而在主线程中,系统已经为我们创建了一个Looper对象,所以不会抛出异常了。
。
而那些会抛出异常报错的情况,是在子线程中创建的handler,但是又没有调用Looper.prepare()去创建Looper对象。
继续前进。
那就来看看,主线程在什么时候创建了Looper对象吧。
在ActivityThread的main方法,这个方法是应用程序的入口。
main方法的源码如下:
publicstaticvoidmain(String[]args){
//代码省略
Looper.prepareMainLooper();
ActivityThreadthread=newActivityThread();
thread.attach(false);
if(sMainThreadHandler==null){
sMainThreadHandler=thread.getHandler();
if(false){
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG,"
ActivityThread"
));
//EndofeventActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
Mainthreadloopunexpectedlyexited"
找到了Looper.prepareMainLooper(),这和Looper.prepare()太像了吧,跟进去看看
publicstaticvoidprepareMainLooper(){
prepare(false);
synchronized(Looper.class){
if(sMainLooper!
thrownewIllegalStateException("
ThemainLooperhasalreadybeenprepared."
sMainLooper=myLooper();
又兜了回来,还是调用了prepare方法的。
所以主线程是已经创建了一个Looper对象的。
Handler的创建过程分析完毕,现在总算搞明白了。
那先总结一下,Handler的创建是依赖于Looper的。
而主线程是默认创建了一个Looper对象的。
每一个Looper会关联一个线程(ThreadLocal中封装了Looper)。
每一个Looper中又会封装一个消息队列。
这样一来,handler,Looper,MessageQueue,Thread四个角色就关联了起来,你中有我,我中有你。
handler在主线程中创建,是因为要和主线程的消息队列关联起来,那样handler的handleMessage方法才会在主线程中执行,那么这样在更新UI就是线程安全的了。
接着继续吧,还很多问题没有解决
相信你更想了解Handler是怎么发送消息的。
通常我们是创建一个Message对象,并将一些从服务端拉取的数据,标记,参数等赋值到Message的一些字段what,arg1,obj等,handler调用sendMessage方法发送,就能将这个数据发送到主线程,然后在handlerMessage方法处理更新UI即可。
那我们就从handler的sendMessage方法开始寻找信息
publicfinalbooleansendMessage(Messagemsg)
{
returnsendMessageDelayed(msg,0);
sendMessage会调用sendMessageDelayed方法并将message对象传进去,第二个参数是延时时间,使用sendMessage方法时默认为0的。
那么来到sendMessageDelayed方法
publicfinalbooleansendMessageDelayed(Messagemsg,longdelayMillis)
if(delayMillis<
0){
delayMillis=0;
returnsendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);
兜兜转转,最终会调用sendMessageAtTime方法,并将message对象传进。
继续跟进sendMessageAtTime方法,
publicbooleansendMessageAtTime(Messagemsg,longuptimeMillis){
MessageQueuequeue=mQueue;
if(queue==null){
RuntimeExceptione=newRuntimeException(
this+"
sendMessageAtTime()calledwithnomQueue"
Log.w("
Looper"
e.getMessage(),e);
returnfalse;
returnenqueueMessage(queue,msg,uptimeMillis);
上面分析了,在创建Looper对象的时候,会创建一个MessageQueue,所以只要Looper是正常创建的话,消息队列是不为空的。
那么到最后一行的enqueueMessage方法,源码如下:
privatebooleanenqueueMessage(MessageQueuequeue,Messagemsg,longuptimeMillis){
msg.target=this;
if(mAsynchronous){
msg.setAsynchronous(true);
returnqueue.enqueueMessage(msg,uptimeMillis);
可以看到最后一行调用了MessageQueue的enqueueMessage方法。
注意:
上面贴出的enqueueMessage是Handler的方法,不是MessageQueue的,只是做了一层包装而已,真正的入队消息队列的操作当然是在MessageQueue中。
而且从第一行的msg.target=this中可以知道,msg的target字段,其实就是handler。
MessageQueue的enqueueMessage方法源码如下:
booleanenqueueMessage(Messagemsg,longwhen){
if(rget==null){
thrownewIllegalArgumentException("
Messagemusthaveatarget."
if(msg.isInUse()){
thrownewIllegalStateException(msg+"
Thismessageisalreadyinuse."
synchronized(this){
if(mQuitting){
IllegalStateExceptione=newIllegalStateException(
msg.target+"
sendingmessagetoaHandleronadeadthread"
Log.w(TAG,e.getMessage(),e);
msg.recycle();
msg.markInUse();
msg.when=when;
Messagep=mMessages;
booleanneedWake;
if(p==null||when==0||when<
p.when){
//Newhead,wakeuptheeventqueueifblocked.
msg.next=p;
mMessages=msg;
needWake=mBlocked;
}else{
//Insertedwithinthemiddleofthequeue.Usuallywedon'
thavetowake
//uptheeventqueueunlessthereisabarrierattheheadofthequeue
//andthemessageistheearliestasynchronousmessageinthequeue.
needWake=mBlocked&
p.target==null&
msg.isAsynchronous();
Messageprev;
for(;
;
){
prev=p;
p=p.next;
if(p==null||when<
break;
if(needWake&
p.isAsynchronous()){
needWake=false;
//invariant:
p==prev.next
prev.next=msg;
//WecanassumemPtr!
=0becausemQuittingisfalse.
if(needWake){
nativeWake(mPtr);
returntrue;
Messagequeue中有一个对象mMessage用于指向当前传进的msg,即最新的消息。
而刚才的sendMessageAtTime(Messagemsg,longuptimeMillis)方法,第二个参数指定了时间,然后在这里按照这个uptimeMillis来进行消息的排序,而我分析的结果msg.next是指向下一个消息,这样每一个消息都是按照时间的排序关联了起来,排在前面的消息指向了排在后面的消息。
以上是进入消息队列的分析,handler调用sendMessage方法的最终将message对象传进messagequeue。
完毕,那么消息是怎么从消息队列出来的呢?
这时我们要回看ActiviryThread的main方法,去寻找点线索。
源码在上面已贴出。
发现了倒数第二行的Looper.loop(),简单理解就是消息循环执行循环操作。
这里一定能满足我们的好奇心。
那么跟进。
loop方法的源码如下:
publicstaticvoidloop(){
finalLooperme=myLooper();
if(me==null){
NoLooper;
Looper.prepare()wasn'
tcalledonthisthread."
finalMessageQueuequeue=me.mQueue;
//Makesuretheidentityofthisthreadisthatofthelocalprocess,
//andkeeptrackofwhatthatidentitytokenactuallyis.
Binder.clearCallingIdentity();
finallongident=Binder.clearCallingIdentity();
Messagemsg=queue.next();
//mightblock
if(msg==null){
//Nomessageindicatesthatthemessagequeueisquitting.
return;
//Thismustbeinalocalvariable,incaseaUIeventsetsthelogger
Printerlogging=me.mLogging;
if(logging!
logging.println("
>
Dispatchingto"
+msg.target+"
msg.callback+"
:
+msg.what);
msg.target.dispatchMessage(msg);
<
Finishedto"
+msg.callback);
//Makesurethatduringthecourseofdispatchingthe
//identityofthethreadwasn'
tcorrupted.
finallongnewIdent=Binder.clearCallingIdentity();
if(ident!
=newIdent){
Log.wtf(TAG,"
Threadidentitychangedfrom0x"
+Long.toHexString(ident)+"
to0x"
+Long.toHexString(newIdent)+"
whiledispatchingto"
+msg.target.getClass().getName()+"
+msg.callback+"
what="
msg.recycleUnchecked();
抓重点看就好。
首先是调用myLooper方法获取到Looper对象,这里是没问题的,那就继续
MessageQueuequeue=me.mQueue
然后从Looper对象中取出关联的消息队列,
接着进入了一个死循环,调用messagequeue的next方法取出message对象。
这个next方法我没看懂,所以不贴源码出来分析了,反正next方法的作用就是取出message对象的。
有兴趣的同学自己去研究研究吧。
到这里可以总结一下:
通过Looper.prepare()来创建Looper(消息循环)对象,然后通过Looper.loop()来执行消息循环,Looper.prepare()和Looper.loop()通常是成对出现的。
好,回来继续
经过一系列的判断后会来到这里,很重点
msg.target.dispatchMessage(msg);
上面已经分析,msg.target就是handler,那么这行代码的意义就是调用handler的dispatchMessgage的方法去分发消息,
那么看到dispatchMessage的方法源码,相信谜底就要揭开了
publicvoiddispatchMessage(Messagemsg){
if(msg.callback!
handleCallback(msg);
if(mCallback!
if(mCallback.handleMessage(msg)){
handleMessage(msg);
从上述的代码跟踪中,都没有发现给message的callback字段赋值,那么我们就先不搭理,默认callback为空,那么就一定会来到handleMessage方法。
message对象传递到了handleMessage方法。
/**
*Su
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android 中的 消息 机制