Handler相关笔记

1,  说说handler机制原理

handler的出现就是为了保证UI线程安全,对UI的修改只有UI线程可以操作,不允许其他线程操作,下面对Handler异步消息处理机制再做一下简单的介绍:

 

1、成员介绍

Message:消息,即线程间传递的对象,传递的信息包含在其中。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。主要功能是进行消息的封装,同时可以指定消息的操作形式;

Looper:消息循环泵,用来为一个线程跑一个消息循环,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。每一个线程最多只可以拥有一个。

MessageQueue:消息队列,用来存放通过Handler发布的消息,按照先进先出执行。每一个线程最多只可以拥有一个。

Handler:消息的处理者,handler 负责将需要传递的信息封装成Message,发送给Looper,继而由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler对象收到该消息后,调用相应的handler 对象的handleMessage()方法对其进行处理。

 

2、同线程各成员的关系及数量

一个线程中只有一个Looper,只有一个MessageQueue,可以有多个Handler,多个Messge;

②一个Looper只能维护唯一一个MessageQueue,可以接受多个Handler发来的消息;

③一个Message只能属于唯一一个Handler;

④同一个Handler只能处理自己发送给Looper的那些Message;

 

Looper主要作用:

(1)      与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。

(2)      loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的Handler的dispatchMessage去处理。

 

3、HandlerThread

(1)HandlerThread本质上是一个线程类,它继承自Thread,只不过其已经初始化内部Looper,可以进行Looper循环;

(2)通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务。

(3)例

           HandlerThread workHandleThread = new HandlerThread("workHandleThread");

           workHandleThread .start();

 

           //在创建Handler作为HandlerThread线程消息执行者的时候必须调用start方法之后,

           //因为创建Handler需要的Looper参数是从HandlerThread类中获得,

           //而Looper对象的赋值又是在HandlerThread的run方法中创建。

           mSubHandler = new Handler(workHandleThread .getLooper(), mSubCallback);

 

           btn.setOnClickListener(new View.OnClickListener() {

                @Override public voidonClick(View v) {

                    //投放异步耗时任务到HandlerThread中

                   mSubHandler.sendEmptyMessage(0);

                }

           });

 

 

注意事项总结

(1)      子线程也可以new handler,但要注意在子线程中显式的调用Looper.prepare()和Looper.loop()方法

(2)      如果一个线程要处理消息,那么它必须拥有自己的Looper,并不是Handler在哪里创建,就可以在哪里处理消息的。

(3)      只有在主线程(handler的Looper为主线程)的Handler可以更新UI,子线程的handler不可以更新UI。

(4)      在创建HandlerThread的Handler作为线程消息执行者的时候必须调用start方法之后,因为创建Handler需要的Looper参数是从HandlerThread类中获得,而Looper对象的赋值又是在HandlerThread的run方法中创建。

 

Thread与Looper

1,每个Handler对应一个Looper和一个MessageQueue(就是该Looper的queue),Looper使用静态方法初始化时获取,因此Looper并不是属于某个handler,而是属于某个线程。
Looper:
每个线程没有或者有一个Looper(用ThreadLocal保存),每个线程的Looper只能被初始化一次(静态方法),主线程的Looper在Android系统初始化时候已经初始化,用户不用管。

MessageQueue:
被Looper持有,在Looper初始化时候被创建,因此每个线程也只有一个

每个线程可以有多个handler,由handler初始化时传入的Looper来决定handler是属于那个线程,Message的target属性记录了处理该消息的Handler
Looper.loop开启消息循环,是一个死循环(也即所属线程的执行全流程,所以Looper主要任务可以理解为是负责的整个线程消息的循环),当MessageQueue为空时,处于阻塞状态,不会无谓的消耗CPU

总结:
每个线程没有或者只有一个Looper,每个Looper初始化时创建一个MessageQueue,只能被初始化一次,所以每个线程只能有一个Looper和一个MessageQueue。
Handler的初始化依赖于Looper,主线程的Looper在Android系统初始化时候已经初始化,子线程Looper通过Looper.prepare静态方法初始化,通过Looper.loop静态方法开启线程消息循环,子线程创建Handler的前题是该线程的Looper已经被初始化。
每个线程可以有多个Handler,由Handler初始化时传入的Looper决定是属于哪个线程,
Message的target属性记录了处理该消息的Handler,在消息循环到时会分发给这个handler处理。

线程Looper实现机制:
每个线程的Looper实例在Looper类用静态ThreadLocal保存,每个线程只有一个,且与其它线程无关

MessageQueue消息入队排序:
MessageQueue的Messages是一个链表结构,根据Message的when排序,sendMessageXXX首先是将msg根据这个when入队


为什么死循环不会导致ANR或CPU过度开销?
答:
ActivityThread的main方法主要作用就是做消息循环,一旦退出消息循环,主线程运行完毕,那么你的应用也就退出了。
Android是事件驱动的,在Loop.loop()中不断接收事件、处理事件,而Activity的生命周期都依靠于主线程的Loop.loop()来调度,所以可想而知它的存活周期和Activity也是一致的。当没有事件需要处理时,主线程就会阻塞;当子线程往消息队列发送消息,并且往管道文件写数据时,主线程就被唤醒。
主线程在没有事件需要处理的时候就是处于阻塞的状态。想让主线程活动起来一般有两种方式:
第一种是系统唤醒主线程,并且将点击事件传递给主线程;
第二种是其他线程使用Handler向MessageQueue中存放了一条消息,导致loop被唤醒继续执行。
主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
 

为什么创建Handler必须在线程Looper.prepare()之后?

(1)代码里,Handler的几个构造方法最终调用有一个参数Looper,该值不能为null。不显示传入该参数的话该Looper是当前线程的Looper。

(2)原理上,Handler是处理MessageQueue的消息的,也即必须有消息循环。MessageQueue属于Looper且在Looper初始化中创建,所以Handler的创建必须在线程有Looper之后,即线程运行中Looper.prepare()之后,Google注释说典型使用如下:

    class LooperThread extends Thread {
        public Handler mHandler;
  
        public void run() {
            Looper.prepare();
  
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
  
            Looper.loop();
        }
    }

一个线程没有或只有一个Looper,一个Looper有一个MessageQueue,一个线程可以有多个Handler,Handler属于哪个线程取决于其Looper变量,初始化时不传入Looper的话,其Looper是该Handler创建时所在的线程中ThreadLocalMap保存的Looper。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值