扣丁学堂解析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培训