1,概念介绍:
handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己。MessageQueue就是一个存储消息的容器。
Message
· 定义:消息,理解为线程间通讯的数据单元(Handler接受和处理的消息对象。)
例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程
Message Queue
通常翻译为“消息队列”,以队列的形式对外提供插入和删除操作。虽然被称之为消息队列,但是实际上它的数据结构却是采用的单链表的结构来存储消息列表(单链表在插入和删除操作上效率比较高)。
MessageQueue主要包含两个操作:插入(enqueueMessage)和读取(next)。
Handler
· 定义:对于handler来说工作主要是消息的发送和接收过程
.作用:负责将Message添加到消息队列&处理Looper分派过来的Message
Looper
定义:循环器,扮演Message Queue和Handler之间桥梁的角色
作用:主要负责消息循环:循环取出Message Queue的Message;消息派发:将取出的Message交付给相应的Handler
一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue
线程是默认没有Looper的,如果需要使用Handler,就必须为线程创建Looper。我们经常提到的主线程,也叫UI线程,它就是ActivityThread,
ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。
2.1,Handler
· 提供sendMessage方法,将消息放置到队列中
· 提供handleMessage方法,定义个各种消息的处理方式;
2.2,Looper
· Looper.prepare():实例化Looper对象;为当前线程生成一个消息队列;
Looper.loop() :循环从消息队列中获取消息,交给Handler处理;此时线程处于无限循环中,不停的从MessageQueue中获取Message 消息 ;如果没有消息就阻塞(死循环)
为什么死循环不会造成程序卡死?
主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/epoll机制,
A, Android为了保证主线程在生命周期内不退出,所以用了无限循环,在循环中,如果没有事件需要处理,则使用epoll进行休眠时监听,休眠会让线程让出CPU,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源.因此节省系统资源。
B, 而在next取消息时,如果当前没有消息要处理,则会调用本地层的Looper的pollOnce来epoll_wait监听之前创建的Pipe,主线程进入休眠状态;
C, 而唤醒则需要别的线程进行唤醒;熟悉Android源码的知道,AMS向Activity发送生命周期消息是通过Binder来实现的,ActivityThread中有Binder的服务器端即ApplicationThread,它是运行在Binder线程中的;当AMS发送消息时,ApplicationThread会通过主线程的Handler向主线程发送消息,通过往pipe里面写入字节,来将epoll_wait唤醒,即将主线程唤醒;
2.3,MessageQueue
· 提供enqueueMessage 方法,将消息根据时间放置到队列中;
· 提供next方法,从队列中获取消息,没有消息的时候阻塞;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.e("date","接收到传递的信息了" );
break;
}
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
}
}).start();
}
1, 每当创建一个handler的时候,就会创建一个MessageQueue来存储消息,接着创建Lopper循环器,在Messagequeue队列里不停的轮训,看看有没有消息,没有消息就一直轮训,当Handler拿到消息,就会通过sendMessage()方法,将message从子线程发送出去,这个时候looper轮训到有消息来,就会将传递出去,传递到handler的handlerMessage()方法中,这个时候就可以在UI线程刷新数据了.
这个MessageQueue队列的结构其实并不是队列,而是采用单链表的数据结构来维护消息队列,因为单链表的数据结构在插入和删除上比较有优势.
2, Handler一般只能只在主线程创建对象,如果在子线程创建对象,会报错,(has not called Looper.prepare())因为Handler工作需要Looper,没有lopper就会报错.在主线程内部自带的调用了Lopper.prepare()方法创建Lopper对象.如果想要在子线程创建对象,就必须自己调用prepare()方法,如果想在子线程接收消息,还得调用Looper.loop()方法不停轮询,才能收到消息.但在事情结束后,要调用quit()来终止消息循环,否则线程就会处于一直等到状态
3,其他重要的类:
ThreadLocal,它是一个可以在指定线程中存储数据的类,数据存储以后,只有在指定的线程中可以获取到存储的数据,在其他线程无法获取该数据,ActivityThread,ActivityManagerService就用到了就用到了ThreadLocal.
4,不同线程有不同的looper
5,Handler是怎么实现线程切换的?
同一个进程中线程和线程之间的资源是 共享的,对于任何变量其他线程都是可以访问和修改的,只要做好同步即可。
拿到主线程的MessageQueue的实例,就可以往主线程的MessageQueue放入消息。Handler是在主线程创建的,通过Handler就可以拿到主线程的MessageQueue实例的引用;Handler 在sendMessage的时候就通过这个引用往消息队列里插入新消息。
1,自身的创建&创建Message Queue:prepare()方法
1,原因:
当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。
而Handler通常伴随着一个耗时任务,若在Activity销毁的时候还有未执行完的任务。它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)
2,解决方法:
2.1,静态static可以解决内存泄漏,静态类不持有外部类的对象,所以你的Activity可以随意被回收。
private static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
}
}
2.2,使用弱引用也可以解决内存泄漏,但是需要等到handler的中任务都执行完,才会释放activity内存,不如直接static释放的快
private static class MyHandler extends Handler {
private final WeakReference mActivity;
public MyHandler(SettingActivity activity) {
mActivity = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
System.out.println(msg);
if (mActivity.get() == null) {
return;
}
}
}
2.3, 在ondestroy()中,使用 handler.removeCallbacksAndMessages(null);
public void onDestory() {
if (handler != null)
handler.removeCallbacksAndMessages(null);
}
当Activity finish后 handler对象还是在Message中排队,还会处理消息,所以在onDestroy的时候,取消掉该Handler对象的Message和Runnable。上面3步结合起来用。
3,handler造成内存泄漏有 两种方案:
一种是业务逻辑上,在activity销毁的时候移除所有未执行的任务。
一种是从GC上,通过static的Handler或者弱引用解决。但是单独的使用弱引用性能不是太高。
handler内存泄漏解决方式
借鉴:
Android开发:Handler异步通信机制全面解析(包含Looper、Message Queue)
Handler机制深入解析
优秀文章:
深入源码解析Android中的Handler,Message,MessageQueue,Looper
Android消息机制1-Handler(Java层)