Skip to content

Handler

DawnSpring edited this page Jan 11, 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放入消息池
    }
}

3) MessageQuene

public final class MessageQueue {
 
//供native代码使用
@SuppressWarnings("unused")
private long mPtr;
 
//交给native层来处理的核心方法
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis); //阻塞操作
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
 
Message mMessages;
 
//消息队列是否可以退出
private final boolean mQuitAllowed;
 
 
//构造方法
MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();  //通过native方法初始化消息队列,其中mPtr是供native代码使用

}

a) next 方法

//不停提取下一条message
Message next() {
    final long ptr = mPtr;
    //判断是否退出消息循环
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; 
    //代表下一个消息到来前,还需要等待的时长
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //native层阻塞cpu。如果被阻塞,唤醒事件队列
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();        
            Message prevMsg = null;
            Message msg = mMessages;
 
            //如果当前消息是异步消息,都将赋值给prevMsg,过滤掉,直到取到了非异步消息
            if (msg != null && msg.target == null) {
              do {
                    prevMsg = msg;
                    msg = msg.next;
              } while (msg != null && !msg.isAsynchronous()); 
            }
            //获取到了非异步消息
            if (msg != null) {
                //任务执行时间大于现在的时间
                if (now < msg.when) {
                //设置下一次轮询的超时时长
        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    mBlocked = false;//指定为非阻塞任务
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //设置消息的使用状态,即flags |= FLAG_IN_USE
                    msg.markInUse();
                    return msg;   //成功地获取MessageQueue中的下一条即将要执行的消息
                }
            } else {
                //表示消息队列中无消息,会一直等待下去
                nextPollTimeoutMillis = -1;
            }
            ......
            //IdleHandler为发现线程何时阻塞的回调接口
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; //去除handler引用
            boolean keep = false;
 
        //queueIdle返回true会被空闲的处理器处理,false就会被移走
            try {
                keep = idler.queueIdle();  //idle时执行的方法
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);  //被移走
                }
            }
        }
        //重置idle handler个数为0,保证不会再次重复运行
        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;
    }
}

next()方法中,做了异步Message消息的判断,特殊的是这个Message没有设置target,即msg.target为null

b)核心的enqueueMessage()方法

boolean enqueueMessage(Message msg, long when) {
    // 每一个普通Message必须有一个target-handler
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    //已在使用状态
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    synchronized (this) {
        //消息在退出状态->被回收到消息池
        if (mQuitting) {  
            msg.recycle();
            return false;
        }
        //标记使用状态,记录执行时间
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //p为null代表MessageQueue没有消息或者msg的触发时间是队列中最早的
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; //当阻塞时需要唤醒
        } else {
            //将消息按时间顺序插入到MessageQueue。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

队列中的Message触发时间是有先后顺序的。当消息加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序(内部遍历队列中Message,找到when比当前Message的when大的Message,将Message插入到该Message之前,如果没找到则将Message插入到队列最后)。一般是当前队列为空的情况下,next那边会进入睡眠,需要的时候MessageQueue这边会唤醒next方法。 C) removeMessages()和removeCallbacksAndMessages()方法

void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }
    synchronized (this) {
        Message p = mMessages;
        //从消息队列的头部开始,移除所有符合条件的消息
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }
        //移除剩余的符合要求的消息
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}
 
 
 
void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }
        synchronized (this) {
            Message p = mMessages;
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
}

4、Handler

a)构造函数

public class Handler {
 
   
   final Looper mLooper;          
   final MessageQueue mQueue;   
   final Callback mCallback;    //回调
   final boolean mAsynchronous;  //是否异步消息
   IMessenger mMessenger;
 
  public interface Callback {
     //如果不需要进一步的处理,则返回True
     public boolean handleMessage(Message msg);
  }
 
  //有参构造
  public Handler(Looper looper) {
    this(looper, null, false);
  }
 
    //有参构造
  public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
  }
 
  public Handler(Callback callback, boolean async) {
    //匿名类、内部类或本地类都必须申明为static,否则会警告可能出现内存泄露
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    //从Looper类中的(ThreadLocal)获取Looper对象
    mLooper = Looper.myLooper();  
    if (mLooper == null) {
        throw new RuntimeException("");
    }
    mQueue = mLooper.mQueue; //Looper取出消息队列
    mCallback = callback;  //回调
    mAsynchronous = async; //设置消息是否为异步处理方式
}

b)消息的发送

1.post--->调用sendMessageDelayed
public final boolean post(Runnable r){
        return  sendMessageDelayed(getPostMessage(r), 0);
}
 
2.postAtTime--->调用sendMessageAtTime
public final boolean postAtTime(Runnable r, long uptimeMillis){
       return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
 
3.postAtTime--->调用sendMessageAtTime
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
 
4.postDelayed--->调用sendMessageDelayed
public final boolean postDelayed(Runnable r, long delayMillis){
        return sendMessageDelayed(getPostMessage(r), delayMillis);
}
 
5.postAtFrontOfQueue--->调用sendMessageAtFrontOfQueue
public final boolean postAtFrontOfQueue(Runnable r){
        return sendMessageAtFrontOfQueue(getPostMessage(r));
}
 
6.sendMessage--->调用sendMessageDelayed
public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
}
 
7.sendEmptyMessage--->调用sendEmptyMessageDelayed
public final boolean sendEmptyMessage(int what){
        return sendEmptyMessageDelayed(what, 0);
}
 
8.sendEmptyMessageDelayed--->调用sendMessageDelayed
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
}
 
 
9.sendEmptyMessageAtTime--->调用sendMessageAtTime
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
}
 
10.sendMessageDelayed--->调用sendMessageAtTime
public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
 
11.sendMessageAtTime--->调用enqueueMessage
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
}
 
12.sendMessageAtFrontOfQueue--->调用enqueueMessage
//FIXME 该方法通过设置消息的触发时间为0,从而使Message加入到消息队列的队头
public final boolean sendMessageAtFrontOfQueue(Message msg) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, 0);
}
 
13.enqueueMessage调用MessageQueue中的enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //uptimeMillis为系统当前的运行时间
        return queue.enqueueMessage(msg, uptimeMillis);
}
if (mAsynchronous) {
     msg.setAsynchronous(true);

c) 消息的移除--都会调用消息队列中的移除方法

public final void removeCallbacks(Runnable r){
        mQueue.removeMessages(this, r, null);
}
 
 
public final void removeCallbacks(Runnable r, Object token){
        mQueue.removeMessages(this, r, token);
}
 
 
public final void removeMessages(int what) {
        mQueue.removeMessages(this, what, null);
}
 
 
public final void removeMessages(int what, Object object) {
        mQueue.removeMessages(this, what, object);
}
 
 
public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
}

d)handleMessage(处理消息)和 dispatchMessage(分发消息)

//处理消息
    public void handleMessage(Message msg) {
    }
    
    //分发消息
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
 
            //当Message存在回调方法,回调方法msg.callback.run()
            handleCallback(msg);
        } else {
 
            //当Handler存在Callback成员变量时,回调方法mCallback.handleMessage();
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
 
            //Handler子类通过覆写该方法来完成具体的逻辑
            handleMessage(msg);
        }
}

优先级:

Message的回调方法>Handler的回调方法>Handler的默认方法

5、总结

  1. Handler通过sendMessage()方法发送Message到MessageQueue队列
  2. 当前Thread中Looper通过调用loop(),不断取出达到触发条件的Message,通过对应target(Handler)的dispatchMessage()方法,将Message交给Handler的handleMessage()方法来处理。
  3. 一个线程对应一个Looper,一个Looper对应一个MessageQueue,一个MessageQueue可以对用多个Message。但是一个Message只能让一个handler来处理(就是Message中target所指定的handler)。

参考:

handler发送异步消息:https://blog.csdn.net/cdecde111/article/details/54670136

https://blog.csdn.net/woshiluoye9/article/details/72544764

http://gityuan.com/2015/12/26/handler-message-framework/

https://blog.csdn.net/iispring/article/details/47180325

https://blog.csdn.net/milllulei/article/details/80927539

https://www.cnblogs.com/angeldevil/p/3340644.html

(IdleHandler)

6、备注:

override fun onDestroy() {
   super.onDestroy()
   handler.removeMessages(123)
}

退出页面时记得removeMessages,否则会报错This message is already in use