Android基础之异步消息处理机制.docx

上传人:b****5 文档编号:8023704 上传时间:2023-01-28 格式:DOCX 页数:12 大小:58.92KB
下载 相关 举报
Android基础之异步消息处理机制.docx_第1页
第1页 / 共12页
Android基础之异步消息处理机制.docx_第2页
第2页 / 共12页
Android基础之异步消息处理机制.docx_第3页
第3页 / 共12页
Android基础之异步消息处理机制.docx_第4页
第4页 / 共12页
Android基础之异步消息处理机制.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

Android基础之异步消息处理机制.docx

《Android基础之异步消息处理机制.docx》由会员分享,可在线阅读,更多相关《Android基础之异步消息处理机制.docx(12页珍藏版)》请在冰豆网上搜索。

Android基础之异步消息处理机制.docx

Android基础之异步消息处理机制

Android基础之异步消息处理机制

今天讲述一下Android的异步消息处理机制,说到异步,我们肯定会想到继承Thread,实现Runnable来处理耗时操作,然后再发消息去处理对应的业务逻辑。

相信大家对下面的代码非常熟悉。

publicclassMainActivityextendsActivity{

privatestaticfinalintMESSAGE=1;

privatestaticHandlermHandler=newHandler(){

@Override

publicvoidhandleMessage(Messagemsg){

//TODOAuto-generatedmethodstub

super.handleMessage(msg);

Log.i("Log","text");

}

};

@Override

protectedvoidonCreate(BundlesavedInstanceState){

//TODOAuto-generatedmethodstub

super.onCreate(savedInstanceState);

CustomThreadthread=newCustomThread();

thread.start();

CustomRunnablerunnable=newCustomRunnable();

runnable.run();

}

privateclassCustomThreadextendsThread{

@Override

publicvoidrun(){

//TODOAuto-generatedmethodstub

super.run();

mHandler.sendEmptyMessage(MESSAGE);

}

};

privateclassCustomRunnableimplementsRunnable{

@Override

publicvoidrun(){

//TODOAuto-generatedmethodstub

mHandler.sendEmptyMessage(MESSAGE);

}

}

}

然而这次的主要内容就是消息处理的原理。

我们首先了解一下以下各元素:

Message:

消息

MessageQuene:

消息队列,可以添加消息,处理消息。

Looper:

消息循环,用于循环取出消息进行处理。

Handler:

发送消息;消息循环从消息队列中取出消息后要对消息进行处理。

我们来看Handler的无参构造方法:

publicHandler(){

this(null,false);

}

publicHandler(Callbackcallback,booleanasync){

if(FIND_POTENTIAL_LEAKS){

finalClass

extendsHandler>klass=getClass();

if((klass.isAnonymousClass()||klass.isMemberClass()||klass.isLocalClass())&&

(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;

}

可以看到,在第10行调用了Looper.myLooper()方法来获取一个Looper对象,如果对象为空,会抛出一个RuntimeException。

在获取到当前线程保存的Looper实例后,再获取了这个Looper实例中保存的MessageQueue(消息队列),这样handler、Looper、MessageQueue三者之间就关联上了。

我们这接着看Looper.myLooper()是怎么处理的。

publicstatic@NullableLoopermyLooper(){

returnsThreadLocal.get();

}

在当前线程会get他的消息循环器Looper,在有get(),就肯定有set(),我们可以想到在Looper.prepare()里面set()。

那么我们继续往下看源码:

publicstaticvoidprepare(){

prepare(true);

}

privatestaticvoidprepare(booleanquitAllowed){

if(sThreadLocal.get()!

=null){

thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");

}

sThreadLocal.set(newLooper(quitAllowed));

}

这里判断是否已经存在Looper,如果不存在则new一个新的Looper,所以说要先有一个消息循环器Looper,才能创建Handler对象。

同时也可以看出每一个线程sThreadLocal只会有一个Looper对象。

我们接着看Looper初始化做了些什么?

privateLooper(booleanquitAllowed){

mQueue=newMessageQueue(quitAllowed);

mThread=Thread.currentThread();

}

MessageQueue(booleanquitAllowed){

mQuitAllowed=quitAllowed;

mPtr=nativeInit();

}

在Looper初始化时,新建了一个MessageQueue的对象,赋予mQueue中,MessageQueue的构造方法访问是包可见,所以我们是无法直接使用的。

然后还有nativeInit(),nativeInit()方法创建NativeMessageQueue对象,并将这个对象的指针复制给AndroidMessageQueue的mPtr。

关于C++中的nativeXXX方法不做过多分析,需要深究的同学自行查阅资料,我们只要明白mPtr为native层的MessageQueue的指针即可。

此时,回到上面,有同学会说,你这个Looper.prepare(),没有在Handler的构造方法出现啊,是怎么回事。

那么我们可以去看一下ActivityThread的main()方法,系统已经帮我们自动调用Looper.prepare()方法了。

代码如下:

publicstaticvoidmain(String[]args){

SamplingProfilerIntegration.start();

CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

EventLogger.setReporter(newEventLoggingReporter());

Process.setArgV0("");

Looper.prepareMainLooper();

ActivityThreadthread=newActivityThread();

thread.attach(false);

if(sMainThreadHandler==null){

sMainThreadHandler=thread.getHandler();

}

AsyncTask.init();

if(false){

Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG,"ActivityThread"));

}

Looper.loop();

thrownewRuntimeException("Mainthreadloopunexpectedlyexited");

}

在第七行调用了Looper.prepareMainLooper()方法。

而这个方法又会再去调用Looper.prepare()方法,在最后又会调用Looper.loop()方法,这就是我们平时创建Handler时不写这两个方法的原因。

接着我们看prepareMainLooper()代码如下所示:

publicstaticvoidprepareMainLooper(){

prepare(false);

synchronized(Looper.class){

if(sMainLooper!

=null){

thrownewIllegalStateException("ThemainLooperhasalreadybeenprepared.");

}

sMainLooper=myLooper();

}

}

这里Main线程(UI线程)初始化消息循环时会调用prepareMainLooper,传进去的是false,消息循环不可以退出,上面说在默认构造方法时可以。

接着是调用myLooper(),创建一个消息循环器Looper,因此我们应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。

这样我们大致理顺了Handler的创建过程。

那么在创建Handler之后,我们是开线程,线程处理完之后用handler发送消息Message,在文章一开头就演示了一个handler.sendEmptyMessage(arg)。

然后我们去翻一下Handler类里面提供了很多发送消息的方法,但是它们最终都是调用sendMessageAtTime(Messagemsg,longuptimeMillis),这里面发送消息的方法我就不贴了,大家可以去翻翻SDK源码验证下,接着我们直接看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);

}

privatebooleanenqueueMessage(MessageQueuequeue,Messagemsg,longuptimeMillis){

msg.target=this;

if(mAsynchronous){

msg.setAsynchronous(true);

}

returnqueue.enqueueMessage(msg,uptimeMillis);

}

mQueue是一个全局变量,在创建Handler的时候,获取到消息循环器Looper之后,会mQueue=mLooper.mQueue。

这里面msg参数就是我们发送的Message对象,而uptimeMillis参数则表示发送消息的时间。

我们看到queue.enqueueMessage()方法,这里是MessageQueue类里面的方法,MessageQueue的作用我们上面也简述过,它是一个消息队列,用于将所有收到的消息以队列的形式进行排列,并提供入队和出队的方法。

源码如下:

finalbooleanenqueueMessage(Messagemsg,longwhen){

if(msg.when!

=0){

thrownewAndroidRuntimeException(msg+"Thismessageisalreadyinuse.");

}

if(msg.target==null&&!

mQuitAllowed){

thrownewRuntimeException("Mainthreadnotallowedtoquit");

}

synchronized(this){

if(mQuiting){

RuntimeExceptione=newRuntimeException(msg.target+"sendingmessagetoaHandleronadeadthread");

Log.w("MessageQueue",e.getMessage(),e);

returnlse;

}elseif(msg.target==null){

mQuiting=true;

}

msg.when=when;

Messagep=mMessages;

if(p==null||when==0||when

msg.next=p;

mMessages=msg;

this.notify();

}else{

Messageprev=null;

while(p!

=null&&p.when<=when){

prev=p;

p=p.next;

}

msg.next=prev.next;

prev.next=msg;

this.notify();

}

}

returntrue;

}

从代码看出,MessageQueue并没有使用一个集合把所有的消息都保存起来,它只使用了一个mMessages对象表示当前待处理的消息。

然后观察上面的代码我们就可以看出,所谓的入队其实就是将所有的消息按时间来进行排序,这个时间当然就是我们刚才介绍的uptimeMillis参数。

具体的操作方法就根据时间的顺序调用msg.next,从而为每一个消息指定它的下一个消息是什么。

现在入队操作我们就已经看明白了,那出队操作是在哪里进行的呢?

这个就需要看一看Looper.loop()方法的源码了,如下所示:

publicstaticvoidloop(){

finalLooperme=myLooper();

if(me==null){

thrownewRuntimeException("NoLooper;Looper.prepare()wasn'tcalledonthisthread.");

}

finalMessageQueuequeue=me.mQueue;

//Makesuretheidentityofthisthreadisthatofthelocalprocess,

//andkeeptrackofwhatthatidentitytokenactuallyis.

Binder.clearCallingIdentity();

finallongident=Binder.clearCallingIdentity();

for(;;){

Messagemsg=queue.next();//mightblock

if(msg==null){

//Nomessageindicatesthatthemessagequeueisquitting.

return;

}

//Thismustbeinalocalvariable,incaseaUIeventsetsthelogger

Printerg=me.mLogging;

if(logging!

=null){

logging.println(">>>>>Dispatchingto"+msg.target+""+

msg.callback+":

"+msg.what);

}

msg.target.dispatchMessage(msg);

if(logging!

=null){

logging.println("<<<<

}

//Makesurethatduringthecourseofdispatchingthe

//identityoftheeadwasn'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.what);

}

msg.recycleUnchecked();

}

}

从一开始myLooper()可以看出如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。

接着在for(;;)看到,进入的是一个死循环,然后不断地调用的MessageQueue的next()方法,这个next()方法就是消息队列的出队方法。

它的简单逻辑就是如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。

每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,msg的target就是handler对象。

【在上面Handler的发送消息enqueueMessage()方法中就首先为meg.target赋值为this】,而Message被处理后会被recycle。

当queue.next返回null时会退出消息循环。

接下来当然就要看一看Handler中dispatchMessage()方法的源码了,如下所示:

publicvoiddispatchMessage(Messagemsg){

if(msg.callback!

=null){

handleCallback(msg);

}else{

if(mCallback!

=null){

if(mCallback.handleMessage(msg)){

return;

}

}

handleMessage(msg);

}

}

在第5行进行判断,如果mCallback不为空,则调用mCallback的handleMessage()方法,否则直接调用Handler的handleMessage()方法,并将消息对象作为参数传递过去。

这样就可以理解到在handleMessage()方法中可以获取到之前发送的消息了。

另外在Looper类开头注释的地方在着官方给的标准异步消息处理线程的写法:

classLooperThreadextendsThread{

publicHandlermHandler;

publicvoidrun(){

Looper.prepare();

mHandler=newHandler(){

publicvoidhandleMessage(Messagemsg){

//processincomingmessageshere

}

};

Looper.loop();

}

}

上述基本把这个流程解释完毕了,我们简单总结一下:

借郭哥图一用:

1.首先Looper.prepare()在当前主线程会创建一个Looper的实例对象,然后该实例中会创建一个MessaheQueue对象,由于Looper.prepare()在线程中有判断是否已存在消息循环器(Looper),存在则不创建,因此一个Looper对应着一个消息队列(MessageQueue)。

2.在Looper.loop()方法中会让当前的线程进入到一个死循环中,不断地从MessageQueue中读取消息,然后回调handler的.dispatchMessage(msg)方法。

3.Handler的构造方法,会首先获取当前线程的Looper对象,由于Looper对应着一个MessageQueue对象,因此这三者建立对应的关联。

4.在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

接着我们来看一下异步消息处理的方式来更新UI线程。

Handlerpost()

newThread(newRunnable(){

@Override

publicvoidrun(){

handler.post(newRunnable(){

@Override

publicvoidrun(){

//在这里进行UI操作

}

});

}

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

当前位置:首页 > 总结汇报 > 学习总结

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

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