从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)

Handler

每一个初学Android开发的都绕不开Handler这个“坎”,为何说是个坎呢,首先这是Android架构的精髓之一,其次大部分人都是知其然殊不知其因此然。今天看到Handler.post这个方法以后决定再去翻翻源代码梳理一下Handler的实现机制。java

异步更新UI

先来一个必背口诀“主线程不作耗时操做,子线程不更新UI”,这个规定应该是初学必知的,那要怎么来解决口诀里的问题呢,这时候Handler就出如今咱们面前了(AsyncTask也行,不过本质上仍是对Handler的封装),来一段经典经常使用代码(这里忽略内存泄露问题,咱们后面再说):网络

首先在Activity中新建一个handler:架构

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    mTestTV.setText("This is handleMessage");//更新UI
                    break;
            }
        }
    };

而后在子线程里发送消息:异步

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);//在子线程有一段耗时操做,好比请求网络
                    mHandler.sendEmptyMessage(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

至此完成了在子线程的耗时操做完成后在主线程异步更新UI,但是并无用上标题的post,咱们再来看post的版本:async

private Handler mHandler;//全局变量
@Override
protected void onCreate(Bundle savedInstanceState) {
    mHandler = new Handler();
    new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);//在子线程有一段耗时操做,好比请求网络
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mTestTV.setText("This is post");//更新UI
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
}

从表面上来看,给post方法传了个Runnable,像是开了个子线程,但是在子线程里并不能更新UI啊,那么问题来了,这是怎么个状况呢?带着这个疑惑,来翻翻Handler的源码:ide

先来看看普通的sendEmptyMessage是什么样子:oop

public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

将咱们传入的参数封装成了一个消息,而后调用sendMessageDelayed:post

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

再调用sendMessageAtTime:this

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);
    }

好了,咱们再来看post():spa

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);//getPostMessage方法是两种发送消息的不一样之处
    }

方法只有一句,内部实现和普通的sendMessage是同样的,可是只有一点不一样,那就是 getPostMessage(r) 这个方法:

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

这个方法咱们发现也是将咱们传入的参数封装成了一个消息,只是此次是m.callback = r,刚才是msg.what=what,至于Message的这些属性就不看了

Android消息机制

看到这里,咱们只是知道了post和sendMessage原理都是封装成Message,可是仍是不清楚Handler的整个机制是什么样子,继续探究下去。

刚才看到那两个方法到最终都调用了sendMessageAtTime

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);
    }

这个方法又调用了 enqueueMessage,看名字应该是把消息加入队列的意思,点进去看下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

mAsynchronous这个异步有关的先无论,继续将参数传给了queue的enqueueMessage方法,至于那个msgtarget的赋值咱们后面再看,如今继续进入MessageQueue类的enqueueMessage方法,方法较长,咱们看看关键的几行:

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;

果真像方法名说的同样,一个无限循环将消息加入到消息队列中(链表的形式),可是有放就有拿,这个消息怎样把它取出来呢?

翻看MessageQueue的方法,咱们找到了next(),代码太长,不赘述,咱们知道它是用来把消息取出来的就好了。不过这个方法是在什么地方调用的呢,不是在Handler中,咱们找到了Looper这个关键人物,我叫他环形使者,专门负责从消息队列中拿消息,关键代码以下:

for (;;) {
     Message msg = queue.next(); // might block
     ...
     msg.target.dispatchMessage(msg);
     ...
     msg.recycleUnchecked();
}

简单明了,咱们看到了咱们刚才说的msg.target,刚才在Handler中赋值了msg.target=this,因此咱们来看Handler中的dispatchMessage:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
  1. msg的callback不为空,调用handleCallback方法(message.callback.run()

  2. mCallback不为空,调用mCallback.handleMessage(msg)

  3. 最后若是其余都为空,执行Handler自身的 handleMessage(msg) 方法

msg的callback应该已经想到是什么了,就是咱们经过Handler.post(Runnable r)传入的Runnable的run方法,这里就要提提java基础了,直接调用线程的run方法至关因而在一个普通的类调用方法,仍是在当前线程执行,并不会开启新的线程。

因此到了这里,咱们解决了开始的疑惑,为何在post中传了个Runnable仍是在主线程中能够更新UI

继续看如果msg.callback为空的状况下的mCallback,这个要看看构造方法:

1.
public Handler() {
        this(null, false);
    }
2.    
public Handler(Callback callback) {
        this(callback, false);
    }
3.
public Handler(Looper looper) {
        this(looper, null, false);
    }
4.
public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
5.
public Handler(boolean async) {
        this(null, async);
    }
6.
public Handler(Callback callback, boolean async) {
        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());
            }
        }

        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;
    }
7.
public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

具体的实现就只有最后两个,已经知道mCallback是怎么来的了,在构造方法中传入就行。

最后若是这两个回调都为空的话就执行Handler自身的handleMessage(msg)方法,也就是咱们熟知的新建Handler重写的那个handleMessage方法。

Looper

看到了这里有一个疑惑,那就是咱们在新建Handler的时候并无传入任何参数,也没有哪里显示调用了Looper有关方法,那Looper的建立以及方法调用在哪里呢?其实这些东西Android自己已经帮咱们作了,在程序入口ActivityThread的main方法里面咱们能够找到:

public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ...
    Looper.loop();
    ...

总结

已经大概梳理了一下Handler的消息机制,以及post方法和咱们经常使用的sendMessage方法的区别。来总结一下,主要涉及四个类Handler、Message、MessageQueue、Looper

  1. 新建Handler,经过sendMessage或者post发送消息,Handler调用sendMessageAtTimeMessage交给MessageQueue

  2. MessageQueue.enqueueMessage方法将Message以链表的形式放入队列中

  3. Looperloop方法循环调用MessageQueue.next()取出消息,而且调用HandlerdispatchMessage来处理消息

  4. dispatchMessage中,分别判断msg.callback、mCallback也就是post方法或者构造方法传入的不为空就执行他们的回调,若是都为空就执行咱们最经常使用重写的handleMessage

最后谈谈handler的内存泄露问题

再来看看咱们的新建Handler的代码:

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

当使用内部类(包括匿名类)来建立Handler的时候,Handler对象会隐式地持有Activity的引用。

而Handler一般会伴随着一个耗时的后台线程一块儿出现,这个后台线程在任务执行完毕后发送消息去更新UI。然而,若是用户在网络请求过程当中关闭了Activity,正常状况下,Activity再也不被使用,它就有可能在GC检查时被回收掉,但因为这时线程还没有执行完,而该线程持有Handler的引用(否则它怎么发消息给Handler?),这个Handler又持有Activity的引用,就致使该Activity没法被回收(即内存泄露),直到网络请求结束。

另外,若是执行了Handler的postDelayed()方法,那么在设定的delay到达以前,会有一条MessageQueue -> Message -> Handler -> Activity的链,致使你的Activity被持有引用而没法被回收。

解决方法之一,使用弱引用:

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);
        }
    }
}
相关文章
相关标签/搜索