转载请注明出处http://blog.csdn.net/ly502541243/article/details/52062179

Handler

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

异步更新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的版本:

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的源码:

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

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:

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

再调用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);

好了,我们再来看post():

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

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

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,这个要看看构造方法:

public Handler() { this(null, false); public Handler(Callback callback) { this(callback, false); public Handler(Looper looper) { this(looper, null, false); public Handler(Looper looper, Callback callback) { this(looper, callback, false); public Handler(boolean async) { this(null, async); 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; 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

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

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

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

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

聊技术 聊电影 聊人生 什么都聊的公众号
聊技术 聊电影 聊人生 什么都聊的公众号

Handler文章关联:

一个线程可以有几个Looper?几个Handler?从Looper.prepare()来看看关于Looper的一些问题

Thread、Handler和HandlerThread关系何在?

AsyncTask你真的用对了吗?

从Handler.postDelayed来看看Android怎么实现处理延时消息

尊重劳动成果,转载请注明出处http://blog.csdn.net/ly502541243/article/details/52062179
2. 延时任务 但是我一直有一个困惑,就是handler.post(r)这个方法有什么独特的作用? 通过看源码发现,post这个方法是把任务r转成一个message放进了handler所在的线程中的messageQueue消息队列中,并且是立刻发送的消息,这样它既不是异步的也不是延时的,所以问题来了: 1. 它和sendMessage()有什么区别? 2. 它有什么独特作用呢? 下结论之前我们先通过源码来分析: * Causes the Runnable r to be 安卓底朝天—— android.os.Handler.post::Runnable: 的调用栈 Handler 中可调用的函数有: android.os.Handler.post::Runnable: android.os.Handler.postAtTime::Runnable,long: android.os.Handler.postAtTime::Runnable,Object,long: android.os.Handler.postDelayed::Runnable,long: android.o 在Android开发中,我们经常会遇到这样一种情况:在UI界面上进行某项操作后要执行一段很耗时的代码,比如我们在界面上点击了一个”下载“按钮,那么我们需要执行网络请求,这是一个耗时操作,因为不知道什么时候才能完成。为了保证不影响UI线程,所以我们会创建一个新的线程去执行我们的耗时的代码。当我们的耗时操作完成时,我们需要更新UI界面以告知用户操作完成了。所以我们可能会写出如下的代码:package ... 网上看了10篇左右的博客,都没有把为什么要用handler.post方法说清楚,云里雾里的。 本文我想说明的是为什么要使用handler.post方法,它和常用的handler.sendmessage方法的区别是什么? 首先,写这篇博客希望大家多多交流与指正,鄙人也不敢保证内容完全正确。 其实写的时候我犹豫了要不要把handlerpost源码搬出来说,会显得更有说服力,但是我看的那些博客都是附带 //do something 用它可以更新一个组件的内容,我们也知道Hanlder中也有一个handler.sendMessage(Message msg)方法,这两个方法有什么区别呢?先看一下Me 可以看到,绑定一个button,点击button后,在异步线程中模拟网络请求,当网络请求结束,改变button上的文字。结果:只有创建视图层次结构的原始线程才能接触其视图。说明除了主线程,其它线程不能更信UI.这个案例引出了两个个问题1.为什么在子线程不能更新UI?因为当多个子线程与主线程共同操作UI时,会出现操作不可控的现象。2.如果想要在子线程更新UI怎么办?这就需要用到我们的Handler了。 1、简介:Handler机制主要为线程间通信而生,主要是为了解决子线程执行完耗时操作后,怎么回调到主(UI)线程的问题。 2、主要组成部分:Handler、Looper、Message和MessageQueue Handler负责发送Message到MessageQueue Looper负责从MessageQueue读取消息,并通过Message的target,调用hanlder的消息分发处理 Handler负责处理消息回调 3、Handler send 消息post 有什么区别... Handler 是一套消息处理机制,是Android SDK来处理异步消息的核心类 Handler有什么作用? Handler 用于子线程与主线程通信。子线程可以通过Handler来通知主线程进行UI更新 Handler如何使用? 参考地址:AndroidHandler用法总结 Android Developers 地址:http://www.android-doc.com/reference/android/os/Handler.html