Handler怎么使用?
1.创建Handler实例。
2.创建Message对象建议使用Message的静态方法obtain()构建。
3.给Message对象赋值。
4.Handler.sendMessage()方法把Message发送出去。
5.在Handler里重写它的handlerMessage方法,用来处理接收到的Message。
Handler机制:
Handler:把Message添加到MssageQueue里,处理Looper发送过来的Message。
Looper:取出MessageQueue的Message,将Message发送到Handler。
Mssage:储存信息。
MessageQueue:储存Handler发送过来的Message
简单说:
当一个应用启动时,会初始化一个UI线程,UI线程中又初始化了Looper,创建Looper的时候又创建了MessageQueue。当Handler把 Messgae发送到MessageQueue里,然后Looper循环的取出发给Handler,由Handler处理这个信息。
Hander,Looper,MessageQueue,Message的全程协作的关系就好比一个餐厅的整体运作关系
Handler好比点餐员
Looper好比后厨厨师长。
MessageQueue好比订单打单机。
Message好比一桌一桌的订单。
接下来我们回顾下我们餐厅点餐的场景,餐厅点餐分为标准点餐和特殊点餐,我们分解来看。
标准流程
1、首先进入一家店,通过点餐员点餐把数据提交到后厨打单机。
2、然后厨师长一张一张的拿起订单,按照点餐的先后顺序,交代后厨的厨师开始制作。
3、制作好后上菜,并标记已做好的订单。
特殊流程
1、订单为延迟订单,比如客人要求30分钟后人齐了再制作,这时会把该订单按时间排序放到订单队列的合适位置,并通过SystemClock.uptimeMillis()定好闹铃。至于为什么用uptimeMillis是因为该时间是系统启动开始计算的毫秒时间,不受手动调控时间的影响。
2、如果打单机中全是延迟订单,则下令给后厨厨师休息,并在门口贴上免打扰的牌子(needWake),等待闹铃提醒,如有新的即时订单进来并且发现有免打扰的牌子,则通过nativeWake()唤醒厨师再开始制作上菜。
3、但是为了提升店铺菜品覆盖,很多相邻的店铺都选择合作经营,就是你可以混搭旁边店的餐到本店吃,此时只需点餐员提交旁边店的订单即可,这样旁边店的厨师长就可以通过打单机取出订单并进行制作和上菜。
总结
一家店可以有多个点餐员,但是厨师长只能有一个。打单机也只能有一个。
映射到以上场景中,一家店就好比一个Thread,而一个Thread中可以有多个Handler(点餐员),但只能有一个Looper(厨师长),一个MessageQueue(打单机),和多个Message(订单)。
根据以上的例子我们类比看下源码,充分研究下整个机制的流程,和实现原理。
Looper的工作流程
ActivityThread.main();//初始化入口 1. Looper.prepareMainLooper(); //初始化 Looper.prepare(false); //设置不可关闭 Looper.sThreadLocal.set(new Looper(quitAllowed)); //跟线程绑定 1.1.Looper.mQueue = new MessageQueue(quitAllowed); //Looper和MessageQueue绑定 1.2.Looper.mThread = Thread.currentThread(); 2. Looper.loop(); 2.1.myLooper().mQueue.next(); //循环获取MessageQueue中的消息 nativePollOnce(); //阻塞队列 native -> pollInner() //底层阻塞实现 native -> epoll_wait(); 2.2.Handler.dispatchMessage(msg);//消息分发
myLooper().mQueue.next()实现原理
通过myLooper().mQueue.next() 循环获取MessageQueue中的消息,如遇到同步屏障 则优先处理异步消息.
同步屏障即为用Message.postSyncBarrier()发送的消息,该消息的target没有绑定Handler。在Hnandler中异步消息优先级高于同步消息。
可通过创建new Handler(true)发送异步消息。ViewRootImpl.scheduleTraversals方法就使用了同步屏障,保证UI绘制优先执行。
Handler.dispatchMessage(msg)实现原理
优先回调msg.callback。
其次回调handler构造函数中的callback。
最后回调handler handleMessage()。
Hander发送消息的流程
1.Handler handler = new Handler();//初始化Handler 1.Handler.mLooper = Looper.myLooper();//获取当前线程Looper。 2.Handler.mQueue = mLooper.mQueue;//获取Looper绑定的MessageQueue对象。2.handler.post(Runnable);//发送消息 sendMessageDelayed(Message msg, long delayMillis); sendMessageAtTime(Message msg, long uptimeMillis); Handler.enqueueMessage();//Message.target 赋值为this。 Handler.mQueue.enqueueMessage();//添加消息到MessageQueue。
MessageQueue.enqueueMessage()方法实现原理
如果消息队列被放弃,则抛出异常。
如果当前插入消息是即时消息,则将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,并通过needWake唤醒Looper线程。
如果消息为异步消息则通过Message.when长短插入到队列对应位置,不唤醒Looper线程。
经常有人问为什么主线程的Looper阻塞不会导致ANR?
首先我们得知道ANR是主线程5秒内没有响应。
什么叫5秒没有响应呢?Android系统中所有的操作均通过Handler添加事件到事件队列,Looper循环去队列去取事件进行执行。如果主线程事件反馈超过了5秒则提示ANR。
如果没有事件进来,基于Linux pipe/epoll机制会阻塞loop方法中的queue.next()中的nativePollOnce()不会报ANR。
对于以上的例子来说,ANR可以理解为用户进行即时点餐后没按时上菜(当然未按时上菜的原因很多,可能做的慢(耗时操作IO等),也可能厨具被占用(死锁),还有可能厨师不够多(CPU性能差)等等。。。),顾客发起了投诉,或差评。但如果约定时间还没到,或者当前没人点餐,是不会有差评或投诉产生的,因此也不会产生ANR。