歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Android主線程的消息系統(Handler\Looper)

前言:

之前的文章寫的都是關於Bitmap和內存的優化技術,這一篇文章給大家談談Handler。

Handler是Android系統中比較重要的一個知識,在Android多線程面試經常會被問到,在實際項目中的確也經常用到。當然也比較復雜,知識比較多,牽扯到的類有Thread、Looper、Message、MessageQueue。

Android是支持多線程的,通常應用程序中與用戶相關的UI事件都是運行在主線程中,比如點擊屏幕、按鈕等,為了保持主線程順暢相應用戶事件不被阻塞就需要把耗時的操作(主要是聯網、操作大文件等)放到子線程中,這個時候你可能會想到Handler(當然還你可以用其他的比如:異步任務,,這個以後再講),但是Handler又是怎麼和Thread聯系起來的呢?這個咱們來看一下Android主線程是怎麼創建的。

ActivityThread:

在ActivityThread.java中有一個main()函數,這個函數就是在一個應用啟動的入口,調用關系是:ActivityManagerService.java中的startProcessLocked函數調用如下代碼:

前言:

之前的文章寫的都是關於Bitmap和內存的優化技術,這一篇文章給大家談談Handler。

Handler是Android系統中比較重要的一個知識,在Android多線程面試經常會被問到,在實際項目中的確也經常用到。當然也比較復雜,知識比較多,牽扯到的類有Thread、Looper、Message、MessageQueue。

Android是支持多線程的,通常應用程序中與用戶相關的UI事件都是運行在主線程中,比如點擊屏幕、按鈕等,為了保持主線程順暢相應用戶事件不被阻塞就需要把耗時的操作(主要是聯網、操作大文件等)放到子線程中,這個時候你可能會想到Handler(當然還你可以用其他的比如:異步任務,,這個以後再講),但是Handler又是怎麼和Thread聯系起來的呢?這個咱們來看一下Android主線程是怎麼創建的。

ActivityThread:

在ActivityThread.java中有一個main()函數,這個函數就是在一個應用啟動的入口,調用關系是:ActivityManagerService.java中的startProcessLocked函數調用如下代碼:

// Start the process.  It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                    app.processName, uid, uid, gids, debugFlags,
                    app.info.targetSdkVersion, null);

Process.start又做了如下的操作,只看方法注釋就行,現在不需要知道具體做了什麼:

/**
    * Start a new process.
    *
    * <p>If processes are enabled, a new process is created and the
    * static main() function of a <var>processClass</var> is executed there.
    * The process will continue running after this function returns.
    *
    * <p>If processes are not enabled, a new thread in the caller's
    * process is created and main() of <var>processClass</var> called there.
    *
    * <p>The niceName parameter, if not an empty string, is a custom name to
    * give to the process instead of using processClass.  This allows you to
    * make easily identifyable processes even if you are using the same base
    * <var>processClass</var> to start them.
    *
    * @param processClass The class to use as the process's main entry
    *                    point.
    * @param niceName A more readable name to use for the process.
    * @param uid The user-id under which the process will run.
    * @param gid The group-id under which the process will run.
    * @param gids Additional group-ids associated with the process.
    * @param debugFlags Additional flags.
    * @param targetSdkVersion The target SDK version for the app.
    * @param zygoteArgs Additional arguments to supply to the zygote process.
    *
    * @return An object that describes the result of the attempt to start the process.
    * @throws RuntimeException on fatal start failure
    *
    * {@hide}
    */
    public static final ProcessStartResult start(final String processClass,
                                  final String niceName,
                                  int uid, int gid, int[] gids,
                                  int debugFlags, int targetSdkVersion,
                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, targetSdkVersion, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
        }
    }

通過注釋也能看到上面的函數會找到ActivityThread的main函數並且執行。main函數中創建了Looper,Looper的作用就是利用線程創建一個消息處理隊列,並且維護這個消息隊列:

 public static void main(String[] args) {
        Looper.prepareMainLooper();//創建Looper
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false);//應用所有的邏輯都在這個方法中
        Looper.loop();//開啟一個消息循環,不斷的讀取MessageQueue中的Message。
    }

Looper:

Looper.prepareMainLooper()的代碼如下:

/**
    * Initialize the current thread as a looper, marking it as an
    * application's main looper. The main looper for your application
    * is created by the Android environment, so you should never need
    * to call this function yourself.  See also: {@link #prepare()}
    */
    public static void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        myLooper().mQueue.mQuitAllowed = false;
    }

上面的方法注釋已經說的很明白,創建了主線程的Looper,這段代碼是系統調用的。先看prepare方法做了什麼操作。

/** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }

 

    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();//獲取當前線程
    }

創建主線程的Looper,每一個Looper對應一個Thread、一個MessageQueue,創建Looper的時候會創建一個MessageQueue。到目前位置創建了應用的主線程(Thread)、Looper、MessageQueue,調用Looper.loop(),開始不斷的從MessageQueue中讀取Message並處理,如果沒有消息則等待。現在有了消息循環,有了管理消息循環的Looper就差發送消息和處理消息的Handler了。

Handler:

這個時候你在你的應用中創建一個Handler,一般都是下面的代碼:

 private static final Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            ..........
            }
        }
    };

這個Handler是在主線程中創建的,Handler的構造函數如下:

 /**
    * Default constructor associates this handler with the queue for the
    * current thread.
    *
    * If there isn't one, this handler won't be able to receive messages.
    */
    public Handler() {
        mLooper = Looper.myLooper();//獲取上面在主線程創建的Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//獲取Looper的MessageQueue
        mCallback = null;//默認為null在後面處理msg時會就行檢查
    }

創建完Handler你就可以用了,比如你發一個消息:

Java

mHandler.sendEmptyMessage(MSG_WHAT);

在系統中會走最終走到Handler.java下面的方法:

/**
    * Enqueue a message into the message queue after all pending messages
    * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
    * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
    * You will receive it in {@link #handleMessage}, in the thread attached
    * to this handler.
    *
    * @param uptimeMillis The absolute time at which the message should be
    *        delivered, using the
    *        {@link android.os.SystemClock#uptimeMillis} time-base.
    *       
    * @return Returns true if the message was successfully placed in to the
    *        message queue.  Returns false on failure, usually because the
    *        looper processing the message queue is exiting.  Note that a
    *        result of true does not mean the message will be processed -- if
    *        the looper is quit before the delivery time of the message
    *        occurs then the message will be dropped.
    */
    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;//注意這行代碼後面會用,把Handler賦值給Msg的target對象
            sent = queue.enqueueMessage(msg, uptimeMillis);//把msg放到MsgQueue中
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

上面的方法第二個是延時毫秒數,queue.enqueueMessage把消息發送到MessageQueue後剩下的就是等待消息被處理,前面不是說了Looper.loop()方法開始輪詢消息隊列嗎,你發送的消息就是在loop方法中讀取到的,讀取到後誰去處理呢?在loop()方法中有一句代碼:

msg.target.dispatchMessage(msg);

msg就是你發送到MessageQueue的消息,被讀取後調用target.dispatchMessage(),這個target就是上面Handler發送消息是賦值的,就是發送消息的Handler本身,然後Handler調用自己的下面方法就行消息處理:

 /**
    * Handle system messages here.
    */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//在這會調用到上面重寫的handleMessage方法。
        }
    }

因為在new Message的時候callback為空,並且Handler的mCallback = null,所以會調用到你上面new Handler時重寫的handleMessage方法。

總結:

每一個線程中都對應一個Looper,每一個Looper都對應一個MessageQueue,這個Looper是用來管理消息隊列的,主要是讀取消息隊列和把消息發送給Message的target去處理。到這你應該清除Thread、Handler、Message、MessageQueue和Looper他們之間的關系了吧。

更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11

Copyright © Linux教程網 All Rights Reserved