Android中handler相关机制的问题补充以及问题纠正

153 阅读2分钟

1.直接在activity中new Handler问题泄漏的原因修正

因为最后所有线程的looper,都会随着对应的线程存入到ThreadLocal中。但是要注意的一点是,在ThreadLocal中,key是弱引用,同时这个value也是可以以一种正确方式进行回收的。因此这可以避免内存泄漏问题的产生。那么真正的原因是什么呢?

private static Looper sMainLooper;  

可以发现主线程对应的looper是static的,因此在主线程中直接new一个handler。会有一个GCRoot,导致内存泄漏。

2.handler中的四类消息类型

2.1 普通的message

这个message就是普通发送的message,target属性不为空,知道谁要处理这个message

2.2 异步message

这类message的优先级较高message的如下方法表明优先级较高

public boolean isAsynchronous() {
    return (flags & FLAG_ASYNCHRONOUS) != 0;
}
private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {
    if (DEBUG_FRAMES) {
        Log.d(TAG, "PostCallback: type=" + callbackType
                + ", action=" + action + ", token=" + token
                + ", delayMillis=" + delayMillis);
    }

    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            scheduleFrameLocked(now);
        } else {
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}

这个异步消息通常用来刷新UI,因此优先级很高

2.3 屏障消息

这类消息是伴随着异步消息的发出而发出的。一个异步消息必然会对应一个屏障消息。这个屏障message的特征是target属性为null。

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

上面的这个是next的部分源码,上面的源码表示,当一个发现队头的消息是屏障消息的时候,就会不断查找“相邻”的一异步消息然后进行处理。当执行完异步消息以后,系统会发出一个消除屏障消息的函数请求,移除这个屏障消息.看如下源码

public void removeSyncBarrier(int token) {
    // Remove a sync barrier token from the queue.
    // If the queue is no longer stalled by a barrier then wake it.
    synchronized (this) {
        Message prev = null;
        Message p = mMessages;
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake;
        if (prev != null) {
            prev.next = p.next;
            needWake = false;
        } else {
            mMessages = p.next;
            needWake = mMessages == null || mMessages.target != null;
        }
        p.recycleUnchecked();

        // If the loop is quitting then it is already awake.
        // We can assume mPtr != 0 when mQuitting is false.
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}

根据token删除屏障消息。

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

上面的代码表示发送异步消息之前一定会发送一个屏障消息,并且异步消息一定在屏障消息后面

private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}

插入屏障消息的时候一定会返回这个屏障消息的token数值