Skip to content

Handler

DawnSpring edited this page Jan 8, 2021 · 18 revisions

参考: https://blog.csdn.net/qq_37321098/article/details/81535449 https://blog.csdn.net/androidsj/article/details/79865091

1. handler用法

发送消息and接收消息,具体用法

//传递的data 
var bundle = Bundle()  
bundle.putString("msg","我可以")

//send data 
private lateinit var handler:Handler  
var message = handler.obtainMessage()
message.data = bundle
message.what = 123
handler.sendMessage(message)

//receive data
**handler = @SuppressLint("HandlerLeak")**
object :Handler(){
    override fun handleMessage(msg: Message){
        super.handleMessage(msg)
        if (msg.what == 123){
        }
    }
}

代码里有句提示: handler = @SuppressLint("HandlerLeak")
表示代码不规范,有内存泄漏的风险,原因:
Handler在Android中用于消息的发送和异步处理,Handler常作为匿名内部类来定义,此时Handler会隐式的持有外部类对象的引用,当外部类关闭时,由于handler持有外部类的引用造成外部类无法被GC回收,这样容易造成内存泄漏;
解决方法
将其定义成一个静态内部类(此时不会持有外部类对象的引用),在构造方法中传入外部类,并对外部类对象增加一个弱引用,外部类关闭后,即使异步消息未处理完毕,外部类也能被GC回收,从而避免内存泄漏。
代码应该这样写:\

//在外部类里这样写:
companion object {
        private class TestHandler(view: FirstFragment) : Handler() {
            private val mView = WeakReference(view)
            override fun handleMessage(msg: Message?) {
                super.handleMessage(msg)
                when (msg?.what) {
                    123 -> {
                       var bundle = msg.data
                       var data = bundle.getString("msg")
                    }
                }
            }
        }
}

另外改下:
private lateinit var handler:TestHandler 

2. 源码

1) Message

public final class Message implements Parcelable {
 
//用户定义的消息代码,以便接收者能够识别
public int what;
 
//arg1和arg2是使用成本较低的替代品-也可以用来存储int值
public int arg1;
public int arg2;
 
//存放任意类型的对象
public Object obj;
 
//消息触发时间
long when;
 
//消息携带内容
Bundle data;
 
//消息响应方
Handler target;
 
//消息管理器,会关联到一个handler
public Messanger replyTo;
 
//回调方法
Runnable callback;
 
//消息存储的链表。这样sPool就成为了一个Messages的缓存链表。
Message next;
 
//消息池
private static Message sPool;
 
//消息池的默认大小
private static final int MAX_POOL_SIZE = 50;
 
 
//从消息池中获取消息
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool; //从sPool的表头拿出Message
            sPool = m.next;  //将消息池的表头指向下一个Message
            m.next = null; //将取出消息的链表断开
            m.flags = 0; // 清除flag----flag标记判断此消息是否正被使用(下方isInUse方法)
            sPoolSize--; //消息池可用大小进行减1
            return m;
        }
    }
    return new Message(); //消息池为空-直接创建Message
}
 
//通过标记判断消息是否正被使用
boolean isInUse() {
   return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
 
//5.0后为true,之前为false.
private static boolean gCheckRecycle = true;
 
 
public void recycle() {
  if (isInUse()) { 
     if (gCheckRecycle) { 
            throw new IllegalStateException("This message cannot be recycled because it is still in use.");
        }
        return;
    }
  recycleUnchecked();  //消息没在使用,回收
}
 
 
//对于不再使用的消息,加入到消息池
void recycleUnchecked() {
    //将消息标示位置为IN_USE,并清空消息所有的参数。
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;  
            sPool = this; //当消息池没有满时,将Message加入消息池
            sPoolSize++; //消息池可用大小加1
        }
    }
}

2) Looper

初始化

public final class Looper {
 
//内部消息队列MessageQueue
final MessageQueue mQueue;
 
//Looper所在的线程
final Thread mThread;
 
//Looper的变量存储
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 
//主looper
private static Looper sMainLooper;
 
 
//私有构造方法,不能通过New实例化。
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建与其绑定的消息队列MessageQueue
        mThread = Thread.currentThread(); //绑定当前线程
}
 
 
//子线程的调用----->最终通过prepare(boolean)实例化Looper
public static void prepare() {
      prepare(true);
}
 
 
//主线程的调用----->最终通过prepare(boolean)实例化Looper
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
      if (sMainLooper != null) {
         throw new IllegalStateException("The main Looper has already been prepared.");
           }
            sMainLooper = myLooper();//存储区中looper作为主looper
        }
}
 
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
}
 
 
//quitAllowed代表是否允许退出,主线程调用为不允许退出,子线程为可退出
private static void prepare(boolean quitAllowed) {
   if (sThreadLocal.get() != null) {
       //看出一个线程只能存在一个Looper-->则调用二次Looper.prepare抛出异常
       throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));//Looper的变量存储+实例化Looper
}

Loop()方法

循环取出messagequeue消息队列中的消息,并分发出去。再把分发后的Message回收到消息池,以便重复利用。


public static void loop() {
    final Looper me = myLooper(); //从存储区拿出looper
    if (me == null) {
       throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;  //获取Looper对象中的消息队列
    ......
    //进入loop的主循环方法
    for (;;) { 
        Message msg = queue.next(); //可能会阻塞 
        if (msg == null) { //没有消息,则退出循环
            return;
        }
        ......
        //target是handler,此处用于分发Message 
        msg.target.dispatchMessage(msg); 
        ......
        msg.recycleUnchecked();  //将Message放入消息池
    }
}

Clone this wiki locally