Android线程间异步通信机制源码分析(3)

private static void prepare(boolean quitAllowed) {
      if (sThreadLocal.get() != null) {
          throw new RuntimeException("Only one Looper may be created per thread");
      }
      sThreadLocal.set(new Looper(quitAllowed));
  }

在创建looper对象的时候也创建了它要轮询的消息队列,并获取了当前线程的引用:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

由于线程和looper对象是一一对应的关系,所以我们有时候判断���前线程是否为UI线程的时候,会调用getMainLooper方法来判断:

public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
}

looper对象持有它所轮询的消息队列的对象,通过loop方法进行轮询:

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;// 获取到当前的消息队列

// 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,有可能会阻塞
            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);
            }
            // 把从队列中取到的message交给handler来处理
            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();
        }
    }

到此为止,在子线程中创建的message对象就被处理好了。下面我们需要谈论一下handler的泄露问题以及解决方法。

Handler泄露问题

谈论整个问题之前,我们需要了解JAVA的GC机制,这里就不做详细介绍了。当我们在activity中创建一个handler对象时,往往会继承一个匿名内部类,里面复写了handler的handleMessage方法。这时,该匿名内部类就会持有当前activity的对象引用。同时,持有该handler的子线程对象往往会进行一些耗时的操作,创建message对象并把handler对象的引用赋给它。此时如果用户关闭了activity,那么此activity对象是应该被系统回收的。但是,由于子线程的耗时操作,并且它和未处理的message对象都持有handler的引用,而handler又持有activity的引用,这就会导致该activity无法回收,出现内存泄露。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/70ed5be6de341d62346977b4a5e06e88.html