【Android】Handler機制、源碼分析、內存泄漏

Handler機制

Handler機制也可說是異步消息機制,由Message,Handler,MessageQueue,Looper組成。
Message:
Message是線程間傳遞的消息,可攜帶少量信息,在不同線程間交換數據。

Handler:
Handler是處理者,可在子線程中發送Message消息,在UI線程中處理Message消息。子線程中發送的Message消息最終傳遞到Handler#handlerMessage()方法進行處理。

MessageQueue:
MessageQueue是消息隊列,用於存放所有Handler發送的消息,每個線程只有一個MessageQueue對象。

Looper:
Looper循環管理MessageQueue中的消息,每當有消息時就將其傳遞給Handler#handlerMessage()方法,每個線程只有一個Looper對象。

整體流程:

  1. Handler發送Message
  2. MessageQueue#enqueueMessage()存放Message->
  3. Looper循環調用MessageQueue#next()取出Message->
  4. Message的處理者Handler調用Handler#dispatchMessage()傳遞消息->
  5. Handler#handlerMessage處理Message

源碼

1. 初始化對象
Handler(Looper looper)和Handler(Looper looper, Callback callback)最終調用Handler(Looper looper, Callback callback, boolean async)將傳入參數賦值

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Handler()與Handler(Callback callback)最終調用Handler(Callback callback, boolean async),這個構造方法裏的Looper是通過Looper.myLooper()去獲取的。

    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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Looper

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

一路追溯下去可以發現是ActivityThread的main方法調用了Looper#prepareMainLooper()
ActivityThread

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

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

Looper#prepareMainLooper()方法調用了prepare(false),將主線程的Looper設置進了ThreadLocal中,Looper.myLooper()獲取的就是main方法設置進去的Looper,這也就是爲什麼無參Handler初始化的對象發送的消息是在主線程中接受。
Looper

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

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

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

2. 發送消息
send消息和post任務最終都是調用Handler#enqueueMessage(),調用方法的時候會把當前Handler設置到Message的target裏用於後續消息傳遞

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

在看對象初始化的時候我們看到在ActivityThread的main方法裏還調用了Looper.loop(),該方法裏進行無限循環,只要獲取到Message就調用target處理者Handler#dispatchMessage()方法

    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;
 		...
        for (;;) {

            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
			...
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
			...
            msg.recycleUnchecked();
        }
    }

Handler裏的dispatchMessage方法就是調用handleMessage方法或執行任務,就可以在實現的handleMessage方法裏或在Runnable的run方法裏處理事件了

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

內存泄漏

以前習慣寫法的Handler會持有Activity的引用,在執行Handler#sendMessageDelay()等設定delay時間的方法時退出,Activity不再被使用,但Handler持有Activity引用,導致Activity無法被回收。
常用解決方法是使用靜態內部類+弱引用的方式:

    private static class MyHandler extends Handler {
        private WeakReference<HandlerActivity> weakReference;

        public MyHandler(HandlerActivity handlerActivity) {
            this.weakReference = new WeakReference<>(handlerActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HandlerActivity activity = weakReference.get();
            if (activity != null) {
                //TODO
            }
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章