您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

深入的Android消息解析机制 {影视源码}

  • 时间:2022-10-25 01:48 编辑: 来源: 阅读:288
  • 扫一扫,手机访问
摘要:深入的Android消息解析机制 {影视源码}
Android应用是由消息驱动的,Android主线程启动时会在内部创建一个消息队列。 然后进入无限循环来轮询是否有新消息要解决。 如果有新信息,解决新信息。 如果没有消息,进入阻塞状态,直到消息周期被唤醒。 那么消息解析机制在Android系统中是如何实现的呢?在开发程序时,我们经常使用处理程序来解析消息。 所以你可以知道Handler是消息解算器,Message是消息体。 此外,还有两个角色:消息队列和消息轮询。 它们分别是MessageQueue和Looper。MessageQueue是消息队列,Looper负责轮询消息。 引言我们已经知道,Android的消息机制主要是通过Handler、Message、MessageQueue和Looper四个类来实现的。 那么它们之间是什么关系呢?其中,消息是消息的主体,负责存储消息的各种信息,包括处理程序对象、消息信息、消息标识等。 MessageQueue是一个消息队列,其中以队列的形式维护一组消息。 处理程序负责发送和解析消息。 Looper轮询消息队列。 Android消息机制创建线程MessageQueue的原理在Android应用中,消息队列(即消息队列)必须在消息解决方案程序运行之前创建。 在主线程中,通过调用Looper类的静态成员函数preparemainLooper()来创建消息队列。 在其余的子线程中,它是通过调用静态成员函数prepare()创建的。 prepareMainLooper()和prepare ():/* *的实现将当前线程初始化为Looper,标记为*应用的主looper。你的应用程序*的主looper是由Android环境创建的,所以你永远不需要*自己调用这个函数。另见:{@ link # prepare ()} *用于初始化主线程中的Looper,由Android环境调用。应该没有客户来电。*/public static void preparemainlooper(){ prepare(false);synchronized(looper . class){ if(smain looper!= null){ throw new IllegalStateException("主活套已经准备好了。");} smain looper = my looper();} } public static @ Nullable Looper my Looper(){ return sthread local . get();} /**将当前线程初始化为循环。*这使您有机会在实际开始循环之前,创建引用*该循环的处理程序。调用此方法后一定要调用* {@link #loop()},通过调用* {@ link # quit ()}结束。*留给客户调用,通过loop()方法启动消息循环。同时,在不需要解消息的时候,需要手动调用退出循环。*/public static void prepare(){ prepare(true);} private static void prepare(boolean quit allowed){ if(sthreadlocal . get()!= null){ throw new runtime exception("每个线程只能创建一个循环");} sthreadlocal . set(new Looper(quit allowed));}在这两个函数调用过程中,使用了sThreadLocal变量。 该变量属于ThreadLocal类型,用于保存当前线程中的Looper对象。 也就是说,Android应用中每创建一个消息队列,都有一个且唯一一个Looper对象与之对应。 而且我们可以从源代码中看到,当对象不唯一时会抛出异常。 private Looper(boolean quit allowed){ mQueue = new message queue(quit allowed);//创建消息队列mThread = thread . current thread();}从上面的源代码可以看出,Looper对象实例化时,会创建一个消息队列。 消息循环过程消息队列建立后,通过调用Looper对象的静态成员方法loop()启动消息循环。 /** *在此线程中运行消息队列。一定要调用* {@link #quit()}来结束循环。*/public static void loop(){ final Looper me = my Looper();if(me = = null){ throw new runtime exception(" No Looper;此线程上未调用Looper.prepare()。);} final message queue queue = me . mqueue;//确保该线程的标识是本地进程的标识,//并跟踪该标识标记实际上是什么。binder . clearcallingidentity();final long ident = binder . clearcallingidentity();for(;;){//开始消息循环Message msg = queue . next();//接收消息时有可能阻塞if(msg = = null){//当消息为null时,退出消息循环返回;} //这必须在局部变量中,以防UI事件设置记录器final Printer logging = me . mlogging;如果(日志记录!= null) {...} final long slow dispatch thresholdms = me . mslowdispatchthresholdms;final long traceTag = me . mtracetag;if (traceTag!= 0 & amp& ampTrace.isTagEnabled(traceTag)) {...}最终长开始= (slowDispatchThresholdMs == 0)?0:system clock . uptime millis();最终长端;请尝试{ msg . target . dispatch message(msg);//求解消息end = (slowdispatchthresholds = = 0)?0:system clock . uptime millis();}最后{ if (traceTag!= 0){ trace . trace end(traceTag);} } if(slowDispatchThresholdMs & gt;0) {最终长时间=结束-开始;如果(时间& gtslowDispatchThresholdMs) {...} } if(日志记录!= null) {...} //确保在调度过程中//线程的标识没有损坏。final long new ident = binder . clearcallingidentity();如果(ident!= newIdent) {...} msg . recycle checked();}}以上源代码是消息循环的过程,消息循环只有在调用loop()方法时才开始工作。 循环开始时:获取当前线程的Looper对象,如果为null,抛出异常;获取消息队列,开始进入消息循环;从MessageQueue中获取消息(调用messagequeue的next()方法),如果为空,则结束循环;否则,继续执行;解析消息并回收消息资源(msg.recycleUnchecked()) 在消息循环的过程中,MessageQueue的next()方法提供消息,没有信息的时候就进入休眠状态,同时解决其他接口。 这个过程非常重要,next()方法也决定了消息循环能否退出。 message next(){ final long ptr = mPtr;//与原生方法相关,当mPtr为0时,返回null,如果(ptr = = 0){返回null;} int pendingIdleHandlerCount =-1;// -1仅在第一次迭代期间int nextPollTimeoutMillis = 0;//0不去睡,-1去写for(;;){ if (nextPollTimeoutMillis!= 0) {//解析当前线程中挂起的Binder进程间通信请求,Binder . flushpendingcommands();} //native方法,该方法在nextPollTimeoutMillis为-1时进入睡眠状态。NativePollence (PTR,nextpolltimeoutmillis);synchronized(this){ final long now = system clock . uptime millis();Message prevMsg = nullMessage msg = mMessages如果(msg!= null & amp& ampmsg.target == null) { //因障碍而停止。查找队列中的下一条异步消息。do { prevMsg = msgmsg = msg.next}而(msg!= null & amp& amp!msg . isasynchronous());}如果(msg!= null){ if(now & lt;msg.when) { //下一条消息未准备好。设置一个超时来唤醒它。nextPollTimeoutMillis =(int)math . min(msg . when-now,整数。MAX _ VALUE);} else { //收到消息。mBlocked = falseif (prevMsg!= null){ prev msg . next = msg . next;} else { mMessages = msg.next} msg.next = nullif (DEBUG) Log.v(TAG,"返回消息:"+msg);msg . markin use();返回msg//return message } } else {//nomore messages . nextpolltimeoutmillis =-1;//升级到睡眠状态}//处理退出消息,因为所有挂起的消息都已处理。//消息循环退出if(MQ iting){ dispose();返回null} //如果第一次空闲,则获取要运行的空闲数。//空闲句柄仅在队列为空或者//队列中的第一条消息(可能是障碍)将在未来处理时运行。if(pendingIdleHandlerCount & lt;0 & amp& amp(mMessages = = null | | now & ltmmessages . when)){ pendingIdleHandlerCount = midle handlers . size();} if(pendingIdleHandlerCount & lt;= 0) { //没有要运行的空闲处理程序。循环,再等一会儿。mBlocked = true继续;} if(mPendingIdleHandlers = = null){ mPendingIdleHandlers = new idle handler[math . max(pendingIdleHandlerCount,4)];} mPendingIdleHandlers = midle handlers . to array(mPendingIdleHandlers);}//运行IdleHandlers。//我们只到达持续第一次迭代的代码块。//求解Idle处理程序接口for(int I = 0;我& ltpendingIdleHandlerCounti++){ final idle handler idler = mPendingIdleHandlers[I];mPendingIdleHandlers[I]= null;//释放对处理程序boolean keep = false的引用;请尝试{ keep = idler . queue idle();} catch (Throwable t) { Log.wtf(标签,“IdleHandler抛出异常”,t);}如果(!keep){ synchronized(this){ midle handlers . remove(idler);} } } //将空闲处理程序计数重置为0,这样我们就不会再次运行它们。pendingIdleHandlerCount = 0;//在调用空闲处理程序时,可能已经传递了新消息//所以不要等待,返回并再次查找挂起的消息。nextPollTimeoutMillis = 0;}}}消息循环退出过程从上面可以看出loop()方法是一个无限循环,只有当MessageQueue的next()方法返回null时循环才会结束。 那么MessageQueue的next()方法什么时候为null呢?在Looper类中,我们看到两个关闭方法quit()和quitSalely()。 两者的区别在于quit()方法直接结束循环,去掉MessageQueue中的所有消息,而quit()只有在解决了消息队列中剩余的非延迟消息时才退出(延迟消息(delayed messages)直接回收)。 这两种方法都调用MessageQueue的quit()方法 void quit(布尔安全){ if(!mqit allowed){ throw new IllegalStateException("主线程不允许退出。");} synchronized(this){ if(mquiting){ return;} m退出=真;//设置退出状态//解析消息if(safe){ RemoveAllFutureMessageLocked();//去掉所有延迟的消息} else { removealmessagestalled();//去掉所有消息} //我们可以假设mPtr!= 0,因为m退出之前为假。native wake(mPtr);//唤醒消息循环}}解决消息队列中的消息:根据safe标志选择不同的解决方案。 /** * API级别1 *清除消息队列中的所有消息*/private void removealmessagelocked(){ message p = m messages;而(p!= null){ Message n = p . next;p . recycle checked();//回收消息资源p = n;} mMessages = null} /** * API Level 18 *去掉消息队列中所有延迟的消息*/private void removal futuremessagelocked(){ finallongnow = system clock . uptime millis();消息p =消息;如果(p!= null){ if(p . when & gt;now){ removeAllMessagesLocked();} else { Message n;for(;;){ n = p . next;if(n = = null){ return;} if(n . when & gt;Now) {//找出延时消息break} p = n;} p.next = null//因为在消息队列中,do {p = n根据消息when(第一条延迟消息之后的所有消息都是由于执行时间排序而延迟的消息);n = p . next;p . recycle checked();//回收消息资源} while (n!= null);}}}消息发送过程在Android应用中,消息通过Handler类发送到线程的消息队列中。 每个处理程序对象包含一个Looper对象和一个MessageQueue对象。 公共处理程序(回调回调,布尔异步){ if(FIND _ POTENTIAL _ LEAKS){ final Class & lt;?扩展处理程序& gtklass = getClass();if((klass . isanonymousclass()| | klass . is member class()| | klass . is localclass())& amp;& amp(klass . get modifiers()& amp;修饰语。STATIC) == 0) { Log.w(TAG,"下面的处理程序类应该是静态的,否则可能会发生泄漏:"+klass . getcanonical name());} } m looper = looper . my looper();//如果(mlooper = = null) {...} m queue = m looper . m queue;//获取消息队列mCallback = callbackmAsynchronous = async}在Handler类中,我们可以看到各种各样的sendMessage方法,最后都调用同一个方法sendMessageAtTime()方法。 public boolean sendmessagetime(Message msg,long uptime millis){ Message queue queue = mQueue;if(queue = = null){ runtime exception e = new runtime exception(this+" sendmessage attime()调用时没有mQueue ");Log.w("Looper ",e.getMessage(),e);返回false}//将消息返回入队消息(queue,msg,uptime millis)添加到消息队列;}私有布尔enqueue Message(Message queue queue,Message msg,long uptime millis){ msg . target = this;if(mAsynchronous){ msg . set asynchronous(true);} return queue . enqueue message(msg,uptime millis);}可以看出这两种方法非常好理解,就是通过MessageQueue对象调用enqueueMessage()方法将消息添加到消息队列中。 布尔入队消息(message msg,long when){//如果(msg。target = = null){ throw new illegalargumentexception("消息必须有目标。");}//消息已被使用if(msg . is inuse()){ throw new illegalstateexception(msg+"此消息已被使用,");} synchronized(this){//if(mquiting){ IllegalStateException e = new IllegalStateException(msg . target+"向死线程上的处理程序发送消息")是否可以退出;Log.w(TAG,e.getMessage(),e);msg . recycle();返回false} msg . markin use();msg.when = when消息p =消息;布尔needWakeif(p = = null | | when = = 0 | | when & lt;P. when) {//newhead,如果阻塞就唤醒事件队列。//如果队列中没有消息,直接加入msg . next = p;mMessages = msg。needWake = mBlocked} else { //插入队列中间。通常我们不需要唤醒//事件队列,除非在队列的最前面有一个障碍//并且该消息是队列中最早的异步消息。needWake = mBlocked & amp& ampp.target = = null & amp& ampmsg . isasynchronous();消息prevfor(;;){ prev = p;p = p . next;//根据执行时间if插入相应位置(p = = null | | when < p . when){ break;} if(need wake & amp;& ampp . isa synchronous()){ need wake = false;} } msg . next = p;//不变量:p = = prev . next prev . next = msg;} //我们可以假设mPtr!= 0,因为m退出为假。if(need wake){ native wake(mPtr);//唤醒消息循环}}返回true}从源代码中可以看出,在消息队列中插入一条消息需要以下步骤:消息持有的Handler对象为null,抛出异常;当消息被使用时,抛出一个异常;当消息队列中没有消息时,直接插入;当消息队列中有消息时,通过比较消息的执行时间,将消息插入相应的位置;确定是否有必要唤醒消息循环。 消息解析过程在消息循环的过程中,如果添加了新的消息,消息将被解析。 从上面的分析可以看出,在消息循环中,目标消息会调用其Handler对象的dispatchMessage()方法,这是解决消息的方法。 /* * *在此处理系统消息。*/public void dispatch message(message msg){//消息回调接口不为空,所以执行回调接口if (msg.callback!= null){ handle callback(msg);} else { if (mCallback!= null) {//Handler回调接口不为null,执行接口方法if(mcall back . handle message(msg)){ return;} } handle message(msg);//Resolve message}}从源代码可以看出,Handler在三种情况下解析消息。 当回调in Message不为空时,执行回调in Message中的方法。 这个回调是一个可运行的接口。 当处理程序中的回调接口不为空时,执行回调接口中的方法。 直接在处理程序中执行handleMessage()方法。 为什么Looper开始调用loop()时主线程没有卡住?经过上面的分析,我们都知道Looper.loop()进入了一个无限循环,那么为什么在主线程中执行这个无限循环的时候主线程没有被卡住呢?或者有人说主线程中剩下的操作可以顺利进行,比如UI操作。 因为Android应用是由消息驱动的,所以Android应用的操作也是通过Android的消息机制来实现的。 这时候就有必要分析一下Android程序启动的入口类ActivityThread。 我们都知道Android程序在启动的时候,在Java层使用ActivityThread的main()方法作为入口,也就是我们所说的主线程。 公共静态void main(String[] args) {.........looper . prepare main looper();activity thread thread = new activity thread();thread . attach(false);//建立一个绑定器通道(创建一个新线程)并与ActivityManagerService建立链接if(smainthreadhandler = = null){ smainthreadhandler = thread . gethandler();} .........looper . loop();抛出new RuntimeException("主线程循环意外退出");}从ActivityThread的main()方法可以看出Looper的初始化和消息循环的开始。同时还有一个关键的方法attach(),用来建立与ActivityManagerService的链接。此处为相应活动中各种事件的发生建立了链接。 还涉及到原生层Looper的初始化。Looper初始化的时候会建立一个管道来维护消息队列的读写,并通过epoll机制(一种IO复用机制)来监控读写事件。 当没有新的消息需要解决时,主线程将被阻塞在管道上,直到有新的消息需要解决;当其他线程向消息队列发送消息时,会通过管道写入数据;我们在调试程序的时候,可以通过函数的调用栈找到真相:这也印证了开头那句话——Android应用是消息驱动的。 是否所有的消息都能在指定的时间开始执行,是指当我们像发送消息队列一样发送一条消息时(比如将一条消息postDelay(action,1000)延迟1000ms),是否会在1000ms后执行该消息? 答案是否定的。 只有Android消息按时间顺序存储在消息队列中。如果我们向队列中添加多个消息,例如10,000个消息,延迟为1000ms,那么最后执行的消息的执行时间与第一个执行的消息的执行时间是不同的。 至此,Android系统的消息解析机制已经分析完毕。 在Android应用中,消息解析主要分为三个过程:在Looper中启动消息循环和开始监控消息队列。 通过处理程序向消息队列发送消息。 通过消息循环调用Handler对象来解决新添加的消息。 使用消息队列时,程序启动时会在主线程中创建消息队列,所以我们在主线程中使用消息机制时,不需要初始化消息循环和消息队列。 在子线程中,我们需要初始化消息队列,注意当不需要消息队列时,要及时调用Looper的quit或quitSafely方法关闭消息循环,否则子线程可能一直在等待。 如果你喜欢这篇文章,不妨给我点个赞,在评论区留言或者转发支持你~点击【GitHub】还有彩蛋!!!


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【技术支持|常见问题】1556原创ng8文章搜索页面不齐(2024-05-01 14:43)
【技术支持|常见问题】1502企业站群-多域名跳转-多模板切换(2024-04-09 12:19)
【技术支持|常见问题】1126完美滑屏版视频只能显示10个(2024-03-29 13:37)
【技术支持|常见问题】响应式自适应代码(2024-03-24 14:23)
【技术支持|常见问题】1126完美滑屏版百度未授权使用地图api怎么办(2024-03-15 07:21)
【技术支持|常见问题】如何集成阿里通信短信接口(2024-02-19 21:48)
【技术支持|常见问题】算命网微信支付宝产品名称年份在哪修改?风水姻缘合婚配对_公司起名占卜八字算命算财运查吉凶源码(2024-01-07 12:27)
【域名/主机/服务器|】帝国CMS安装(2023-08-20 11:31)
【技术支持|常见问题】通过HTTPs测试Mozilla DNS {免费源码}(2022-11-04 10:37)
【技术支持|常见问题】别告诉我你没看过邰方这两则有思想的创意广告! (2022-11-04 10:37)

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部