博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Handler、Message、Looper、MessageQueue
阅读量:4068 次
发布时间:2019-05-25

本文共 10242 字,大约阅读时间需要 34 分钟。

文章目录

一、什么是handler

在这里插入图片描述

二、handler消息机制原理

handler消息机制原理:本质就是在线程之间实现一个消息队列(MessageQueue)。

生产者Handler在异步线程通过sendMessageDelayed() 将消息添加至MessageQueue,
消费者Looper通过loop()中死循环将MessageQueue中的msg取出后发送给产生此msg的handler的handleMessage() 在主线程进行处理;

三、Handler消息机制组件

Handler

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发送至消息队列中

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

public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            // 接受msg的线程处理消息            handleMessage(msg);        }    }

Message

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

private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }
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

//底部是链表的形式获取msgMessage 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种使用方式:

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()方法:

public Handler(Callback callback) {        this(callback, false);    }public interface Callback {        public boolean handleMessage(Message msg);    }

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

1、定时运行某一线程

例1:

private Runnable mNextRunnable = new Runnable() {		@Override		public void run() {			mHandler.removeCallbacks(mNextRunnable);			if(isCycle()){				setCurrentItem(getCurrentItem() + 1, true);				mHandler.postDelayed(mNextRunnable, mDelayTime);			}		}	};

例2:

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加到队列中

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被持有引用而无法被回收。

举例:

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自然会在合适的时候被回收。

private void recycle() {        mExecutor.shutdownNow();    }
2.消息对象从消息队列移除

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

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

例:

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

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

static class MyHandler extends Handler {    @Override    public void handleMessage(Message msg) {        mImageView.setImageBitmap(mBitmap);    }}

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

static class MyHandler extends Handler {    WeakReference
mActivityReference; MyHandler(Activity activity) { mActivityReference= new WeakReference
(activity); } @Override public void handleMessage(Message msg) { final Activity activity = mActivityReference.get(); if (activity != null) { mImageView.setImageBitmap(mBitmap); } }}

转载地址:http://uhaji.baihongyu.com/

你可能感兴趣的文章
MongoDB数据库插入、更新和删除操作详解
查看>>
MongoDB文档(Document)全局唯一ID的设计思路
查看>>
mongoDB简介
查看>>
Redis持久化存储(AOF与RDB两种模式)
查看>>
memcached工作原理与优化建议
查看>>
Redis与Memcached的区别
查看>>
redis sharding方案
查看>>
程序员最核心的竞争力是什么?
查看>>
Node.js机制及原理理解初步
查看>>
linux CPU个数查看
查看>>
分布式应用开发相关的面试题收集
查看>>
简单理解Socket及TCP/IP、Http、Socket的区别
查看>>
利用HTTP Cache来优化网站
查看>>
利用负载均衡优化和加速HTTP应用
查看>>
消息队列设计精要
查看>>
分布式缓存负载均衡负载均衡的缓存处理:虚拟节点对一致性hash的改进
查看>>
分布式存储系统设计(1)—— 系统架构
查看>>
MySQL数据库的高可用方案总结
查看>>
常用排序算法总结(一) 比较算法总结
查看>>
SSH原理与运用
查看>>