Handler、Message、Looper、MessageQueue

2022-06-25 10:28:06 浏览数 (1)

文章目录

  • 一、什么是handler
  • 二、handler消息机制原理
  • 三、Handler消息机制组件
    • Handler
    • Message
    • Looper
    • MessageQueue
  • 三、Handler的三种使用方式
    • 1、定时运行某一线程
    • 2、1不满足时,然后当callBack不为空时,会回调callBack的handleMessage()方法
    • 3、以上1、2都不满足时,调用handler的 handleMessage() 方法;
  • 四、Handler的内存泄漏问题
    • 原因:
    • 解决办法
      • 方法一:程序逻辑代码。
      • 1.在关闭Activity的时候停掉你的后台线程。
      • 2.消息对象从消息队列移除
      • 方法二:将Handler声明为静态类,activity使用弱引用赋值进去

一、什么是handler

二、handler消息机制原理

handler消息机制原理:本质就是在线程之间实现一个消息队列(MessageQueue)。 生产者Handler在异步线程通过sendMessageDelayed() 将消息添加至MessageQueue, 消费者Looper通过loop()中死循环将MessageQueue中的msg取出后发送给产生此msg的handler的handleMessage() 在主线程进行处理;

三、Handler消息机制组件

Handler

代码语言:javascript复制
public Handler(Callback callback, boolean async) {

       //mLooper每个线程只有唯一一个Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

sendMessageDelayed将msg发送至消息队列中

代码语言:javascript复制
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis()   delayMillis);
    }


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);
    }
   
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

looper将msg分发到这里,然后接受消息的线程处理msg

代码语言:javascript复制
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 接受msg的线程处理消息
            handleMessage(msg);
        }
    }

Message

代码语言:javascript复制
public final class Message implements Parcelable {
  
  /**
     *用户定义的消息代码,以便收件人可以识别此消息的内容。每个{@link Handler}都有自己的消息代码名称
     *空间,因此您不必担心与其他处理程序冲突。
     */
    public int what;

    /**
     * 可选的Messenger,可以发送对此消息的回复。确切如何使用它的*语义取决于发送者和接收者。
     */
    public Messenger replyTo;

    /**
     * 可选字段,指示发送消息的uid。这仅对{@link Messenger}发布的消息有效;否则,它将是-1。
     */
    public int sendingUid = -1;
    
    /*package*/ int flags;

    /*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;

    // 我们存储这些东西的链表
    /*package*/ Message next;

Looper

代码语言:javascript复制
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
代码语言:javascript复制
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to "   msg.target   " "   msg.callback   ": "   msg.what);
            }
            try {
            
                //lopper在这里根据msg.target(handler)分发msg
                
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

MessageQueue

代码语言:javascript复制
//底部是链表的形式获取msg
Message next() {
       
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        for (;;) {
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: "   msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
    }

三、Handler的三种使用方式

由以下代码知道,handler有3种使用方式:

代码语言:javascript复制
Handler中looper最后把msg发送到该方法:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
           //1、线程定时运行
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //2、mCallback的回调方法
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //3、handler的方法
            handleMessage(msg);
        }
    }

private static void handleCallback(Message message) {
        message.callback.run();
    }

1)、定时运行某一线程:public final boolean post(Runnable r) 2)、1)不满足时,然后当callBack不为空时,会回调callBack的handleMessage()方法:

代码语言:javascript复制
public Handler(Callback callback) {
        this(callback, false);
    }

public interface Callback {
        public boolean handleMessage(Message msg);
    }

3)、以上1)、2)都不满足时,调用handler的 handleMessage()方法;

1、定时运行某一线程

例1:

代码语言:javascript复制
private Runnable mNextRunnable = new Runnable() {
		@Override
		public void run() {
			mHandler.removeCallbacks(mNextRunnable);
			if(isCycle()){
				setCurrentItem(getCurrentItem()   1, true);
				mHandler.postDelayed(mNextRunnable, mDelayTime);
			}
		}
	};

例2:

代码语言:javascript复制
new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            Activity activity = (Activity)context;
                            if (activity != null && !activity.isFinishing()) {
                                ToastUtil.makeToast(context,“ OK ”, Toast.LENGTH_SHORT).show();
                            }
                        }
                    }, 1000);

由以下代码知道:当时间到了才将msg加到队列中

代码语言:javascript复制
MessageQueue中:
 boolean enqueueMessage(Message msg, long when) {       
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
           //当时间到了才将msg加到队列中
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

2、1不满足时,然后当callBack不为空时,会回调callBack的handleMessage()方法

3、以上1、2都不满足时,调用handler的 handleMessage() 方法;

四、Handler的内存泄漏问题

原因:

1、当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。 2、另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。

举例:

代码语言:javascript复制
public class LeakCanaryActivity extends AppCompatActivity

    private  Handler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

            }
        };

        Message message = Message.obtain();
        message.what = 1;
        mHandler.sendMessageDelayed(message,10*60*1000);
    }

}

解决办法

方法一:程序逻辑代码。
1.在关闭Activity的时候停掉你的后台线程。

线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。

代码语言:javascript复制
 private void recycle() {
        mExecutor.shutdownNow();
    }
2.消息对象从消息队列移除

如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

代码语言:javascript复制
/**
     *删除消息队列中任何待处理的Runnable
     */
    public final void removeCallbacks(Runnable r)
    {
        mQueue.removeMessages(this, r, null);
    }

例:

代码语言:javascript复制
    private void recycle() {
        if (mHandler != null) {
            mHandler.removeCallbacks(null);
        }
    }
方法二:将Handler声明为静态类,activity使用弱引用赋值进去

静态类不持有外部类的对象,所以你的Activity可以随意被回收。代码如下:

代码语言:javascript复制
static class MyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        mImageView.setImageBitmap(mBitmap);
    }
}

但其实没这么简单。使用了以上代码之后,你会发现,由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference):

代码语言:javascript复制
static class MyHandler extends Handler {
    WeakReference<Activity > mActivityReference;

    MyHandler(Activity activity) {
        mActivityReference= new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        final Activity activity = mActivityReference.get();
        if (activity != null) {
            mImageView.setImageBitmap(mBitmap);
        }
    }
}

0 人点赞