1. 程式人生 > >一個執行緒多個handler會有多少個looper,looper如何區分handler,會不會導致訊息錯亂。

一個執行緒多個handler會有多少個looper,looper如何區分handler,會不會導致訊息錯亂。

面試題:

問題1:一個執行緒中初始化多個handler,會產生多少個looper?

問題2:如果只有一個looper,looper如何區分handler,handler傳送了訊息會不會導致Looper錯亂,最終不知道誰處理。

1 一個執行緒中初始化多個handler,會產生多少個looper

分析一下:做過android開發的都知道Handler是android的訊息機制,在主執行緒可以直接使用handler,那是因為主執行緒已經預設幫我們初始化了Looper,呼叫了Looper.prepare()和loop(),我們可以在主執行緒定義多個handler都不用自己生成或繫結Looper,所以一個執行緒只有一個Looper,可能大家會信這個分析,來看看Looper原始碼:

執行緒只有一個Looper:

 // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

public static void prepare() {
        prepare(true);
    }


    //如果不為null,會報異常,所以一個執行緒只能呼叫一次prepare,生成一個looper
    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:

這裡的主執行緒就是UI執行緒,Activity由ActivityThread啟動,會呼叫ActivityThread的main函式:

//ActivityThread.java 
public static void main(String[] args) {
       //呼叫了Looper的prepareMainLooper

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        //呼叫了loop,開始迴圈
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
 public static void prepareMainLooper() {
        prepare(false);//初始化looper,放入sThreadLocal
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

 //返回和當前執行緒關聯的Looper
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

對於Looper.loop()後面會進行分析

2 一個looper,多個Handler,handler傳送的訊息利用dispatchMessage處理時如何區分

首先說明,一個handler傳送的訊息只會被自己接收,所以是可以正常處理的(就不demo演示了)

傳送訊息除了利用Handler之外還有Message,Message一般利用Obtain獲取,其實obtain中還可以傳遞引數,可以接收Message,還可以接收Handler,message有多個屬性,常用的有what,arg1,arg2,data等,其實還有一個屬性叫做target,這個target屬性就是標識handler的。

handler傳送message一般呼叫sendMessage:

Handler.java

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

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

 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) {
//會把this賦值給msg.target,此時target就指向當前Handler
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
//之後呼叫MessageQueue的enqueueMessage分發訊息進行處理
        return queue.enqueueMessage(msg, uptimeMillis);
    }

最終是Looper取Messagequeue中的訊息,交給Handler處理:

Looper.java

 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;

        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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {

    //////這裡可以很明顯的看到呼叫message.target.dispatchMessage
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

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

分析完畢!