参考文章:
1.https://www.jianshu.com/p/1c79fb5296b6
2.https://www.jianshu.com/p/9e4d1fab0f36
3.https://www.cnblogs.com/cheneasternsun/p/5467115.html
四要点
1.Handler 消息执行者
2.Message 消息
3.MessageQueue 消息列队
4.Looper 消息泵
使用说明:主要是为了线程间通信而设计,常用于子线程发送消息给主线程处理ui消息。
前提说明:
1.由于在主线程的入口,也就是ActivityTherad.main()中,系统已经帮我们把主线程变成looper线程。也就是说在主线中有个looper已开启循环
public static void main(String[] args) {
......
Looper.prepareMainLooper();//为主线程创建Looper,该方法内部又调用 Looper.prepare()
......
Looper.loop();//开启消息轮询
......
}
2.Looper.prepare()说明,该方法主要是给当前本地线程sThreadLocal设置一个looper
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true));
}
3. Looper.loop() 该方法内开启 死循环,获取当前绑定的MessageQueue,并通过queue.next方法,来获取下一个线程,如果下个线程为空,或者需要延迟,则进入阻塞状态。
public static void loop() {
final Looper me = myLooper();
......
final MessageQueue queue = me.mQueue;
......
for (;;) {
Message msg = queue.next(); // might block
.......
msg.target.dispatchMessage(msg);
......
msg.recycleUnchecked();
}
}
进入Hnadler使用案例:
1.在主线程new Handler(), 这一步 主要是关联当前线程的looper,并把关联的loop中的messageQueue作为自己的MQ。
public Handler(Callback callback, boolean async) {
.....
mLooper = Looper.myLooper(); // 默认将关联当前线程的looper
.....
mQueue = mLooper.mQueue; //重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
mCallback = callback;
mAsynchronous = async;
}
2.在工作线程(子线程)中调用Handler.sendMessage(msg),最终会调用下面这行代码
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //message中的target关联到当前handler
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); //把消息添加到消息列队
}
3.当Handler.sendMessage(msg)后,主线程中的Looper.loop()方法中的循环,就会抽取出message,然后调用 msg.target.dispatchMessage(msg),让与msg关联的hanlder来分发消息
4.最后来看一下dispatchMessage(msg),该方法中Handler的mCallback一般为空,那么就会执行handleMessage,这里的handleMessage就是我们new Handler()的时候重写的方法,这就完成了整个从子线程发送消息到主线程了。
public class Handler {
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
}
注意点:
1.一个线程只能由一个looper。因为在创建loop必须调用Looper.prepare(),这一个会获取当前线程然后给他设置一个新的looper()
2.Looper有一个MessageQueue,可以处理来自多个Handler的Message;
3.MessageQueue有一组待处理的Message,这些Message可来自不同的Handler
4.与第三点相同,也就是说,一个线程可以由多个Handler
5.handler.obtainMessage(),是从msg池:sPool中获取一个Message对象,就能避免创建对象,从而减少内存的开销了。
handler造成内存内漏
出现原因:因为handler隐士持有activity引用。(内部内隐士持有外部类引用)
举例:假设一个子线程未运行完毕,这个时候,当前activity已经关闭了,该子线程又持有handler引用,handler又持有ac引用,这就造成了gc无法回收ac,导致内存泄漏
解决方案:
方法一:通过程序逻辑来进行保护。
1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
2.如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。
方法二:将Handler声明为静态类。
PS:在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。
静态类不持有外部类的对象,所以你的Activity可以随意被回收。由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference)。
代码如下:
static class MyHandler extends Handler
{
WeakReference
public MyHandler(Activity activity)
{
mWeakReference=new WeakReference
}
@Override
public void handleMessage(Message msg)
{
final Activity activity=mWeakReference.get();
if(activity!=null)
{
if (msg.what == 1)
{
noteBookAdapter.notifyDataSetChanged();
}
}
}
}
WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),
该对象就会在被GC检查到时回收掉。对于上面的代码,用户在关闭Activity之后,就算后台线程还没结束,但由于仅有一条来自Handler的弱引用指向Activity,所以GC仍然会在检查的时候把Activity回收掉。这样,内存泄露的问题就不会出现了。