扣丁学堂解析Android 异步消息分发机制

2018-09-04 10:36:53 391浏览

随着互联网IT行业的发展,越来越多的人选择入坑互联网,程序员是一个很好的选择,但是很多没有基础的同学都会选择先去培训机构培训,今天小编先来给大家讲一下Android异步消息分发机制。



Android的消息机制

–主要是指Handler的运行机制和MessageQueue、looper的工作过程。

MessageQueue

MessageQueue消息队列,用来存储消息,虽然称为消息队列,但是它的存储结构是采用单链表的数据结构来存储消息队列的。

包含两个操作插入(enqueueMessage)和读取(next)

enqueueMessage:往消息链表中插入一条信息

next:从消息列表中读取一天消息,并将其从消息队列中移除,next方法是一个无限循环的方法,如果消息列表里没有消息,那么会一直阻塞在这里。

Looper

Looper消息循环,用来处理消息。

Looper会不停的从MessageQueue中查看是否有新的消息,如果有新消息就会立即处理

Looper.java

privateLooper(booleanquitAllowed){

mQueue=newMessageQueue(quitAllowed);

mThread=Thread.currentThread();

}

在Looper的构造方法会创建一个MessageQueue,消息队列,然后将当前线程的对象保存起来。

publicstatic@NullableLoopermyLooper(){

returnsThreadLocal.get();

}

privatestaticvoidprepare(booleanquitAllowed){

if(sThreadLocal.get()!=null){

thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");

}

sThreadLocal.set(newLooper(quitAllowed));

}

问题为什么Looper.prepare()不能被调用两次?

答:因为每次调用Looper.prepare()的时候的都会判断ThreadLocal是否已经存储当前的Looper对象,如果已经存在就会抛出异常。

/**

*Runthemessagequeueinthisthread.Besuretocall

*{@link#quit()}toendtheloop.

*/

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

finalPrinterlogging=me.mLogging;

if(logging!=null){

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

msg.callback+":"+msg.what);

}

finallongtraceTag=me.mTraceTag;

if(traceTag!=0&&Trace.isTagEnabled(traceTag)){

Trace.traceBegin(traceTag,msg.target.getTraceName(msg));

}

try{

msg.target.dispatchMessage(msg);

}finally{

if(traceTag!=0){

Trace.traceEnd(traceTag);

}

}

if(logging!=null){

logging.println("<<<<<Finishedto"+msg.target+""+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.what);

}

msg.recycleUnchecked();

}

}

loop方法就是一个死循环,跳出死循环的方法,是MessageQueue的Next方法为空,即queue.next()为空。Looper调用quit方法或者quitSafely方法,通知消息队列退出,会使MessageQueue的next方法返回为空。

loop调用MessageQueue的next方法获取新的消息,如果没有消息,next方法会一直阻塞在那里,导致loop方法也会一直阻塞在那里。如果next方法返回了新的消息,Looper就会处理新的消息,调用msg.target.dispatchMessage(msg),msg.target就是发送这条消息的Handler对象(Handler中有讲解)。这样就把代码逻辑切换到指定线程去执行了。

3.ThreadLocal

线程内部的数据存储类,通过它可以在指定的线程存储数据,而且存储后,只能在指定线程获取存储的数据,其他线程无法获取。

应用:当某些数据是以线程为作用域,并且不同线程具有不同数据

存在Looper中,并不是线程,作用是在每个线程中存储数据

问题:Handler内部是如何获取当前线程的Looper的?

Handler.java

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;

}

答:Handler通过ThreadLocal获取每个线程的Looper.ThreadLocal可以在不同线程中互不干扰的存储并提供数据。

在调用Looper.prepare()的时候“sThreadLocal.set(newLooper(quitAllowed));”-创建了一个Looper实例并将一个Looper的实例放入了ThreadLocal存储。然后在Handler中调用“mLooper=Looper.myLooper();”-从sThreadLocal.get()获取到Looper对象。

问题:主线程中为什么可以默认使用Handler?

答:线程默认是没有Looper的,如果要使用Handler,就必须为线程创建Looper.但是主线程,也就是UI线程,它就是ActivityThread,ActivityThread被创建的时候默认会初始化Looper,当前UI线程调用了Looper.prepare()和Looper.loop()方法.

4.Handler

主要工作:发送和接收消息;

publicfinalbooleansendMessage(Messagemsg)

{

returnsendMessageDelayed(msg,0);

}

publicfinalbooleansendMessageDelayed(Messagemsg,longdelayMillis)

{

if(delayMillis<0){

delayMillis=0;

}

returnsendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);

}

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

}

由上面可以看出Handler发送消息的过程就是向消息链表中插入了一条消息,MessageQueue的next方法会返回这条消息给Looper,Looper收到消息,通过msg.target.dispatchMessage(msg)交给Handler处理。

上面enqueueMessage方法首先“msg.target=this;”会把this赋值给msg.target,也就是说把Handler赋值给msg的target属性。

publicvoiddispatchMessage(Messagemsg){

if(msg.callback!=null){

handleCallback(msg);

}else{

if(mCallback!=null){

if(mCallback.handleMessage(msg)){

return;

}

}

handleMessage(msg);

}

}

首先会检查Message的callback是否为null,不为空就处理通过handleCallback来处理消息。

callback是一个Runnable接口,就是Handler的post方法传递的Runnable参数

privatestaticvoidhandleCallback(Messagemessage){

message.callback.run();

}

举个栗子第一种handler的使用方式,post方法

HandlermHandler=newhandler();

mHandler.post(newRunnable()

{

@Override

publicvoidrun()

{

Log.e("TAG",Thread.currentThread().getName());

mTxt.setText("yoxi");

}

});

其次查看mCallback是否为空,mCallback是个接口

publicinterfaceCallback{

publicbooleanhandleMessage(Messagemsg);

}

Callback提供另一种使用Handler的方式,不想派生Handler的子类,可以通过Callback来实现。

举个栗子第二种使用Handler的方式

privateHandlermHandler=newHandler()

{

publicvoidhandleMessage(android.os.Messagemsg)

{

switch(msg.what)

{

casevalue:

break;

default:

break;

}

};

};

最后,调用Handler的handleMessage方法来处理消息。


以上就是扣丁学堂Android在线学习小编给大家分享的Android模拟器相关的内容,希望对小伙伴们有所帮助,想要了解更多内容的小伙伴可以登录扣丁学堂官网咨询。想要学好Android开发小编给大家推荐口碑良好的扣丁学堂,扣丁学堂有专业老师制定的Android学习路线图辅助学员学习,此外还有与时俱进的Android课程体系和大量的Android视频教程供学员观看学习,想要学好Android开发技术的小伙伴快快行动吧。

扣丁学堂微信公众号

【关注微信公众号获取更多学习资料】



查看更多关于“Android开发技术的相关资讯>>


标签: Android培训

热门专区

暂无热门资讯

课程推荐

微信
微博
15311698296

全国免费咨询热线

邮箱:codingke@1000phone.com

官方群:148715490

北京千锋互联科技有限公司版权所有   北京市海淀区宝盛北里西区28号中关村智诚科创大厦4层
京ICP备12003911号-6   Copyright © 2013 - 2019

京公网安备 11010802030908号