[TOC]

简介

android的消息处理有三个核心类:Looper,Handler和Message,
主要接受子线程发送的数据, 并用此数据配合主线程更新UI。
部分图片来至CodingMyWorld博客,3Q

使用方法

public class LooperThread extends Thread {    @Override    public void run() {        // 将当前线程初始化为Looper线程        Looper.prepare();        // ...其他处理,如实例化handler        // 开始循环处理消息队列        Looper.loop();    }}

通过上面两行核心代码,你的线程就升级为Looper线程了,就具备消息处理的功能!

Looper.prepare()

Handler源码详解及导致内存泄漏的分析_第1张图片
通过上图可以看到,现在你的线程中有一个Looper对象,它的内部维护了一个消息队列MQ。注意,一个Thread只能有一个Looper对象,以下源码使用到ThreadLoacal,可以想象成一个线程的属性/变量,想了解更多请点击链接

    public static void prepare() {        prepare(true);    }    private static void prepare(boolean quitAllowed) {        //获取当前线程对应线程变量:Looper,重复执行此方法会有如下报错提示        //        //"Only one Looper may be created per thread"        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        //将当前初始化的Looper对象保存到当前线程变量中        sThreadLocal.set(new Looper(quitAllowed));    }

Looper.loop()

Handler源码详解及导致内存泄漏的分析_第2张图片
调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。

 /**     * Run the message queue in this thread. Be sure to call     * {@link #quit()} to end the loop.     */    public static void loop() {        //获得当前线程的Looper对象        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;        // Make sure the identity of this thread is that of the local process,        // and keep track of what that identity token actually is.        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        //循环消息体        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            Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            //向目标对象中分发当前循环到的消息体,下面将会具体讲解            msg.target.dispatchMessage(msg);            if (logging != null) {                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);            }            // Make sure that during the course of dispatching the            // identity of the thread wasn't corrupted.            final long newIdent = Binder.clearCallingIdentity();            if (ident != newIdent) {                Log.wtf(TAG, "Thread identity changed from 0x"                        + Long.toHexString(ident) + " to 0x"                        + Long.toHexString(newIdent) + " while dispatching to "                        + msg.target.getClass().getName() + " "                        + msg.callback + " what=" + msg.what);            }            //使用完消息之后需要对它进行回收            msg.recycleUnchecked();        }    }

msg.target.dispatchMessage(msg)解释

Message类中查找可以发现

    /*package*/ Handler target;

其实target就是handler对象,那handler是如何和一个Message发生联系的,稍等?下面移步Handler源码分析

Handler的创建就已经获取了当前线程的Looper和消息队列

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());            }        }        //获取同一线程的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;    }

真正Message和Handler关联的地方来了

在我们sendMessage()的时候都是先用obtainMessage 来获取一个Message

    public final Message obtainMessage()    {        return Message.obtain(this);    }

移步Message的方法看详细

    public static Message obtain(Handler h) {        Message m = obtain();        //是不是so easy,真正的联系在这里        m.target = h;        return m;    }

如果你是使用post(Runnable r) 来发送消息的,那应该构造一个Message来发出去,不信可以看源码
Handler类中:

    public final boolean post(Runnable r)    {       return  sendMessageDelayed(getPostMessage(r), 0);    }    /**    *构造一个Message    */    private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        //与PostMessage不同的是这个runable是给了callback属性        m.callback = r;        return m;    }

具体消息处理:Handler处理消息

    /**     * Handle system messages here.     */    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            //处理Runable消息            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            //处理具体Message,这个是你在消息处理的时候重写的方法实现            handleMessage(msg);        }    }

总结

  1. Lopper–消息的集合和消息的循环
  2. Handler–消息的管理接口和消息的处理
  3. 主线程已经持有Looper,所以不需要Looper.prepare()
    Activity源码查看:
    /*package*/ ActivityThread mMainThread;

ActivityThread源码中查看:

    final Looper mLooper = Looper.myLooper();

导致内存泄漏的分析

内存泄漏场景

  1. 在一个activity中post已个message
  2. 关闭这个activity
  3. 由于某些原因这个message开始执行或者正在执行(如上一个message比较耗时/当前message比较耗时),
    更严重的是你发送一个延时消息前把activity关闭
    参考代码
private Handler mLeakHandler=new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            Logger.d(msg.toString());        }    };    @Override    protected void onResume() {        super.onResume();        //延迟10s来模拟场景        mLeakHandler.sendEmptyMessageDelayed(0x1,10000);        finish();    }    //省略其他代码

分析及修改方法

由于这个Handler作为内部类声明在Activity内部,普通的内部类对象隐式地保存了一个指向外部类对象的引用,所以这个Handler对象保存了一个指向Activity对象的引用。而这个Handler对象的生命周期可能比Activity生命周期长,比如当有一个后台线程持有该Handler,且该线程在执行一个长时间任务。Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity不会被回收。但是注意这个泄漏时临时的!当这个消息处理完引用关系也就不存在了,下次GC的时候也就能回收啦
修改方法:

private static class MyHandler extends Handler {        private WeakReference reference;        public MyHandler(Activity activity) {            reference = new WeakReference(activity);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            LeakActivity activity = (LeakActivity) reference.get();            if (activity != null) {                Logger.d("activity != null"+activity.toString());            } else {                Logger.d("activity = null");            }        }    }    private final Handler mHandler = new MyHandler(this);

同时你需要在调用一下方法,避免不必要的回调(虽然不会报错了)

    @Override    protected void onDestroy() {        super.onDestroy();        mHandler.removeCallbacksAndMessages(null);    }

参考文献:

  1. http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html
  2. http://blog.csdn.net/qjyong/article/details/2158097
  3. http://m.blog.csdn.net/article/details?id=51493352 (代码来源博客)

更多相关文章

  1. Android主线程、子线程通信(Thread+handler)
  2. 【翻译】Android多线程下安全访问数据库
  3. Android子线程真的不能更新UI么
  4. [Android]Thread线程入门1
  5. Android保证首次获取到的location对象不为空的解决方案

随机推荐

  1. 深入了解Android图形管道-part2
  2. ORB_SLAM2在android studio上用cmake编译
  3. Android(安卓)有效的解决内存泄漏的问题
  4. Android GPS 获取
  5. Android bitmap内存优化测试
  6. Android 中 动画效果实现
  7. Android(java)学习笔记99:android的短信发送
  8. Android多点触控(图片的缩放Demo)
  9. android Multiple markers at this line
  10. Android通过共享用户ID来实现多Activity