Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android源碼分析--Handler和Looper機制詳解

Android源碼分析--Handler和Looper機制詳解

編輯:關於Android編程

在Android系統中的應用程序,與Java的應用程序相同,都是靠消息驅動,簡單的說就是:有一個消息隊列,我們可以不斷的向這個消息隊列中添加消息,並從中取出消息,處理消息。Android中與此工作相關的主要是由Handler,Looper以及Message來完成。

Looper類:為一個線程運行著一個消息循環,內部有一個消息隊列,每一個線程只允許最多存在一個Looper; Handler類:允許你向一個線程的消息隊列中發送消息,處理消息; Message類:消息類。

使用樣例

首先,我們通過一個簡單的例子來學習如何使用,Looper和Handler,並通過這個例子來研究一下其工作原理;
1.我們在LooperThread中為消息循環做准備,並創建一個Handler用於處理消息,注意Handler的創建要在調用Looper.prepare()之後;

public class LooperThread extends Thread {

    public Handler mHandler;

    @Override
    public void run() {
        // TODO Auto-generated method stub
        Looper.prepare();
        synchronized (this) {
            mHandler = new Handler() {

                @Override
                public void handleMessage(Message msg) {
                    // TODO Auto-generated method stub
                    super.handleMessage(msg);
                    Log.w("LooperThread", "handleMessage::Thread id---" + getId());
                }

            };
        }
        Looper.loop();
        notifyAll();
    }

}

2.接下來我們在主線程中創建一個新的線程,並通過LooperThread中的Handler向LooperThread線程發送消息;

    final LooperThread mLooperThread = new LooperThread();
    mLooperThread.start();
    new Thread() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (mLooperThread.mHandler == null) {
                try {
                    wait();//防止在發送消息時Handler還沒建立
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            mLooperThread.mHandler.sendEmptyMessage(0);
            Log.w(TAG, "Send Message::Thread id ---" + getId());
        }
    }.start();

該例子主要是新創建一個Thread,並在該線程中向LooperThread發送消息。為了防止在發送消息時Handler還沒建立,進行了同步,在mHandler為null時,阻塞該線程以等待,當建立了Handler後喚醒該線程。可以在log中看到結果:
\

Looper分析

由上面的例子我們可以看到如果我們想在本線程中進行消息管理,首先需要調用Looper.prepare方法,那就讓我們一起首先來看一下這個方法:

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

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

可以看到在prepare方法中首先判斷了該線程中是否已經創建了Looper,接著調用了ThreadLocal的set方法。ThreadLocal是Java中的線程局部變量類,它使得各線程能夠保持各自獨立的一個對象,一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。對於ThreadLocal機制在這裡不做過多贅述。

在這裡我們為該線程set了一個Looper,接下來然我們看看Looper的構造函數:

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

可以看到,在構造方法中,我們創建了一個新的消息隊列並設置了當前的線程。

接下來我們創建了一個Handler,關於Handler我們一會了分析,在最後我們調用了Looper的loop方法用來進行消息循環。讓我們來看看這個方法:

    public static void loop() {
        //myLooper()方法就是通過sThreadLocal.get()返回我們剛剛設置的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;

        // 確保線程和本地進程是一致的,並且記錄這個identity token
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // 可能會阻塞
            if (msg == null) {
                // 沒有消息則表明這個消息隊列退出了.
                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);
            }

            // 確保在分發的過程中該線程沒有崩潰
            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.recycle();
        }
    }

可以看到,loop方法主要是從消息隊列中不斷的取出消息,並將該消息分發出去。

總結一下,Looper的工作:

封裝了一個消息隊列, 利用prepare將Looper和調用prepare方法的線程聯系起來 利用loop函數分發消息

Handler分析

知道了Looper的工作,讓我們來看看如何把消息發送出去。首先來看看它的構造方法,

    public Handler(Callback callback, boolean async) {
        ......
        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;
    }

無參數的構造方法調用了this(null, flase)構造方法,可以看到,在在Handler的成員變量中有一個Looper,首先獲取了當前創建Handler的線程的Looper,另外可以看到在Handler中也保存了一個消息隊列最終指向了Looper的消息隊列。

當我們調用了sendMessage方法之後就向Looper發送了一條消息,讓我們看看這個方法,消息是如何被傳遞的。sendMessage方法最終會調用到sendMessageAtTime方法來:

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

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

可以看到,sendMessage方法最終調用了queue.enqueueMessage方法將消息加入到了Looper中的消息隊列。

在上面我們看到了我們分發消息是調用了dispatchMessage方法:

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

可以看到,dispatchMessage設置了一套消息處理的優先機制:

如果Message自帶了Callback,則交給Message的Callback處理; 如果Handler了設置了Callback,則交給Handler的Callback處理; 如果兩者都沒有,則調用handleMessage方法處理。

Handler和Looper的同步問題

在上面的例子中我們可以發現,Handler和Looper是存在同步問題的,如果在LooperThread中Handler還沒創建起來,在第二個線程中就發送了消息,這樣就會引發空指針異常。在上面,我參考了Android中的HandlerThread,利用了wait/notifyAll的方法解決了這個問題,在實際使用時我們完全可以使用HandlerThread來完成。

    final HandlerThread mHandlerThread = new HandlerThread("LooperThread");
    mHandlerThread.start();
    final Handler mHandler = new Handler(mHandlerThread.getLooper()){

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            Log.w("LooperThread", "handleMessage::Thread id---" + mHandlerThread.getId());
        }

    };

    new Thread() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            mHandler.sendEmptyMessage(0);
            Log.w(TAG, "Send Message::Thread id ---" + getId());
        }
    }.start();

我們可以來看一下HandlerThread的源碼:

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

可以看到,這個的思想和我們剛剛例子中所用的是一致的。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved