当前位置: 首页 > 科技观察

Android线程间通信的Handler

时间:2023-03-12 03:58:08 科技观察

本文讨论Android的handler机制。相信写过android的童鞋,对handler一定不陌生。因为使用频率太高了。特别是当非ui线程想要刷新ui控件时。因为ui控件的刷新只能在主线程进行,但是我们可能有在非ui线程更新ui的需求。比如后台线程下载的一张图片,需要更新到ui。这时候就需要主线程handler来发送了。更新消息。Handler的使用频率很高,我们需要知道它的内部是如何工作的。一句话概括handlerthreadhandlersendwhattriggeredthreadcreatehandlerrunnableencapsulation如何处理messagemessage如何生成message发送定时loopercreatelooperdispatchmessage例子总结一句话总结handler,looper和message的结合,可以是什么完毕?简单来说,简单来说,一句话:在一个线程中,指定在另一个线程中执行一个任务。处理程序线程什么是处理程序线程。当一个线程创建了一个looper,这个looper有一个消息队列,并且创建了一个handler,那么这个线程就是一个handler线程。handler线程的作用是让其他线程指定handler线程来执行一个任务。例如,ui线程是一个处理程序线程。我们可以指定ui线程在普通线程中更新ui。handlerhandler有两个工作,一个是发送任务或者消息;另一个是处理消息或执行任务。发送什么处理程序?可以发送什么处理程序与消息队列密切相关。直观上,处理程序会将消息发送到消息队列。事实上,不仅如此,handler可以同时发送message和runnbale。也就是说,消息队列不仅仅是一个消息队列(实际上是一个单向链表),还是一个runnable。triggeredthreadhandler发送消息或任务,一般是其他线程发送的,即发送消息的线程不是创建handler的线程(当然你也可以在创建的线程上发送消息handler,相当于发给自己)。处理程序在它自己创建的线程中处理消息或执行任务。创建HandlerHandler和Looper并不是UI线程独有的。任何普通线程都可以创建自己的循环器和处理程序。但是需要注意的是,在创建handler之前,必须先创建一个looper。如果不创建looper,直接新建一个handler即可,如newThread(newRunnable(){publicvoidrun(){Handlerhandler=newHandler();}}).start();运行时会直接报错:Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()查看handler的构造函数publicHandler(Callbackcallback,booleanasync){...mLooper=Looper.myLooper();//Looper.myLooper是用来获取当前线程的looperif(mLooper==null){thrownewRuntimeException("Can'tcreatehandlerinsidethreadthathasnotcalledLooper.prepare()");}mQueue=mLooper.mQueue;...}handler向消息队列发送消息,所以在构造handler时,你必须知道消息队列以确定将消息发送到哪里。消息队列由循环器管理,因此按顺序,必须在创建处理程序之前创建循环器。创建一个线程Looper,Looper.prepare();因此,正确的创建处理程序的方法是:newThread(newRunnable(){publicvoidrun(){Looper.prepare();Handlerhandler=newHandler();}}).start();可能有同学会觉得奇怪,在使用newHandler()的时候,不用先调用Looper.prepare()就可以使用?那是因为处理程序是在主线程中创建的。//ActivityThread.javapublicstaticvoidmain(String[]args){...Looper.prepareMainLooper();...}当主线程启动时,会调用Looper.prepareMainLooper()创建一个looper,所以我们可以创建一个looper在主线程中直接创建handler,无需先手动创建looper。runnable的封装可能大家比较陌生。消息队列应该保存消息,那么handler.post(runnable)中的runnable跑到哪里去了呢?runnable其实是发送到消息队列中的,只是在发送之前,将runnable进行了封装。publicfinalbooleanpost(Runnabler){returnsendMessageDelayed(getPostMessage(r),0);}privatestaticMessagegetPostMessage(Runnabler){Messagem=Message.obtain();m.callback=r;returnm;}使用getPostMessage将runnable包装成一个消息,消息回调它是可运行的。因此,要区分消息是否可运行,只需要检查消息的回调是否为空即可。如果为空,则为正常消息;否则,这是一个runnbale。如何处理消息见dispathMessagepublicvoiddispatchMessage(Messagemsg){if(msg.callback!=null){handleCallback(msg);//handler.post(runnable)gohere}else{if(mCallback!=null){//handler=newHandler(callback)gohereif(mCallback.handleMessage(msg)){return;}}handleMessage(msg);//handler=newHandler()gohere}}根据handler发送的消息类型,可以分为2种情况:handler向消息队列发送消息handler向消息队列发送一个runnbale根据前述,message.callback实际上是对runnable的封装,所以如果handler向消息发送一个runnablequeue,就会执行这个runnable。如果处理程序向消息队列发送消息,则细分为两种情况。callback是在handler创建的时候设置的,即handler=newHandler(callback);handler创建时没有设置回调,即handler=newHandler();如果设置了回调,消息将首先被回调处理。如果回调返回true,则表示处理完成,不会传递给handler.handleMessage。如果回调返回false,表示处理没有完成,消息会传递给handler.handleMessage继续处理。如果没有设置回调,消息将直接传递给handler.handleMessage进行处理。message如何生成messagemessage如何生成message可以通过constructor来生成。大多数时候直接从可回收消息对象池中获取,以提供性能。从消息对象池中获取消息的方式是Message.obtain(),或者Handler.obtainMessage()。此外,可以在不生成消息的情况下发送消息。你是什??么意思?handler.sendEmptyMessage()可以在不构造消息对象的情况下向消息队列发送空消息。什么时候发送消息什么时候发送消息,处理时间还是由handler决定的(感觉handler很宽==)。handler.sendMessage将消息放在消息队列的尾部,looper从前到后一条一条的取消息。handler.sendMessageAtFrontOfQueue,将消息放在消息队列的头部,消息可以立即处理。handler.sendMessageAtTime,不会立即将消息发送到消息队列,而是在指定的时间点发送。handler.sendMessageDelayed,不立即将消息发送到消息队列,而是在指定的延时后发送。Looper创建looper前面说过,looper就像一个sender,它会从消息队列中取出消息,然后派发给handler进行处理。因此,要知道应该将消息发送到哪个处理程序,必须首先创建一个循环程序。创建循环器方法Loop.prepare();通过looper.loop()分发消息,looper会不断从消息队列中取消息并发送出去。Looper如何知道应该将消息分派给哪个处理程序?我们来看看循环方法publicstaticvoidloop(){...for(;;){Messagemsg=queue.next();//mightblockif(msg==null){//nomessageindicatesthatthemessagequeueisquitting.return;}...msg.target.dispatchMessage(msg);...}}每个msg都有一个target属性,这个target就是发送消息的handler,分发消息就是分发给msg.target对象。循环方法不是无限循环。一旦消息队列为空,就结束,以免长时间占用cpu资源。示例在下图中,线程A是处理程序线程。现在线程B要线程A处理一条消息message5。因此,线程B得到线程A的handler引用,然后调用handler的sendMessage。消息5被发送到线程A的消息队列。线程A如何处理这条消息?使用looper.loop方法,每次都会从消息队列中取出一条消息。当message5被taken时,就意味着message5即将被处理。真正的消息处理逻辑是在handler的handleMessage(或者runnable,callback,这里以handleMessage为例)中自定义的。Looper拿到消息5,通过消息5的target属性知道目标handler,然后将消息发送给handler进行处理。综上所述,处理程序不是独立存在的。处理程序必须有一个专用线程、一个消息队列和一个与其关联的循环程序。这些角色如何协同工作?可以简单概括为以下四个步骤:处理程序向消息队列发送消息。这个消息可能是一个消息或者一个runnablelooper负责从消息队列中获取消息。looper将消息分派给handler处理程序处理消息(handleMessage或执行runnable)。handler和looper的关系有点类似于生产者和消费者的关系。handler就是producer,生产消息,然后加入到消息队列中;Looper是消费者,它从消息队列中获取消息。