Android消息機制

Android的消息機制

​ 消息驅動模式是android提供的用來更新UI的一套機制,即消息處理的機制,類似於Windows的消息機制,所有的外部來的按鍵消息、觸屏消息、各種系統Intent、廣播等都轉化爲內部消息,然後在主線程中分發處理。在Windows的消息處理模型中,存在一個系統消息隊列,這個隊列是整個進程的核心,幾乎所有的動作都要轉化成消息,然後放在隊列中,消息的處理只能在主線程中完成。

​ Android的消息處理則不太一樣。Android沒有全局的消息隊列,消息隊列是和某個線程關聯在一起的。每個線程最多有一個消息隊列,消息的取出和處理在線程中完成。

爲什麼要是用消息機制

​ Android在一個程序啓動後會創建一個主線程,也叫UI線程(非線程安全)這個線程主要負責監聽屏幕點擊事件與界面繪製,不可以直接在子線程修改UI。之所以設計成單線程模型的UI主線程,是因爲多個線程訪問UI可能會導致衝突,造成界面顯示錯亂,例如,子線程A和子線程B同時修改一個組件的尺寸和背景等資源的情況,如果非要用多線程同步加鎖機制更新UI又會導致性能下降。Android在設計的時候,提供了一種異步回調機制,在子線程中用Handler通知UI線程顯示、更新UI。同時,Android系統也會將大部分耗時的任務(網絡訪問訪問數據庫)交給子線程處理,當子線程完成任務將通過Handler將結果回傳給UI線程,顯示任務的結果。這種機制同時避免的ANR(Application Not Responding),即應用無響應。

消息機制涉及的類

Looper : 消息循環,Looper內部有一個消息隊列MessageQueue,默認情況下一個線程不包含一個消息循環,需要自己去創建,調用Looper.prepare()創建一個消息循環,調用Looper.Looper()執行這個循環,Android啓動時,爲主線程(UI線程)創建了一個Looper對象。


Message :消息,定義一個包含描述和任意數據對象的消息發送給一個Handler,包含兩個int數據區域和一個對象數據區域。消息的創建最好用Message.obtain()Handler.obtainMessage() 方法,從一個消息池中回收。


MessageQueue :消息隊列,持有一個Looper分發的消息列表,Message不是直接添加到消息隊列裏的,是添加到Handler對象綁定的Looper的對象中。


Handler : 發送和處理消息(MessageRunnable對象),與一個線程的MessageQueue關聯。一個Handler實例與一個線程(包括線程的消息隊列)綁定。當一個Handler實例被創建時,該實例與創建它的線程/線程的消息隊列 綁定到一起,然後向該消息隊列發送MessageRunnable,並且當發送的MessageRunnable從消息隊列返回時處理它們。

Looper類

主要成員和方法

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  
    final MessageQueue mQueue;
    final Thread mThread;
    public static void prepare() 
    public static void prepareMainLooper() 
    public static Looper getMainLooper() 
    public static void loop() 
}

prepare()

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類的實例對象,Looper類的實例必須通過prepare()方法來創建。prepare()方法會創建一個Looper對象,並把它保存在靜態變量mTreadLocal中,一個線程多次調用prepare()會拋出異常。

靜態變量sThreadLocal

​ 該變量是一個ThreadLocal類型,即線程本地存儲區(TLS),每個線程都有自己的私有的本地存儲區域,不同線程之間彼此不能訪問對方的TLS區域。它通過將需要保存的對象和線程id關聯在一起的方式實現了線程本地存儲功能,作用就是將Looper類線程隔離,保證每個線程只能有一個Looper對象。

ThreadLocal.set(T value):

public void set(T value) {
      Thread currentThread = Thread.currentThread(); //獲取當前線程
      Values values = values(currentThread); //查找當前線程的本地儲存區
      if (values == null) {
          //當線程本地存儲區,尚未存儲該線程相關信息時,則創建Values對象
          values = initializeValues(currentThread);
      }
      //保存數據value到當前線程this
      values.put(this, value);
  }

ThreadLocal.get():

public T get() {
      Thread currentThread = Thread.currentThread(); //獲取當前線程
      Values values = values(currentThread); //查找當前線程的本地儲存區
      if (values != null) {
          Object[] table = values.table;
          int index = hash & values.mask;
          if (this.reference == table[index]) {
              return (T) table[index + 1]; //返回當前線程儲存區中的數據
          }
      } else {
          //創建Values對象
          values = initializeValues(currentThread);
      }
      return (T) values.getAfterMiss(this); //從目標線程存儲區沒有查詢是則返回null
  }

loop()

    public static void loop() {
        final Looper me = myLooper();//獲取本地存儲區的Looper對象
        ……
        final MessageQueue queue = me.mQueue;//消息隊列
        ……
        for (;;) {
            Message msg = queue.next(); // might block,這裏可能阻塞
            if (msg == null) {
                return;//沒有消息退出
            }
            ……
            msg.target.dispatchMessage(msg);//消息分發
            ……
            msg.recycleUnchecked();//消息回收
        }
    }

loop()是一個靜態方法,其中有一個無限的for循環,loop()方法會循環從消息隊列MessageQueue中取出消息,然後分發出去。消息的分發是通過Message中的target變量完成的,這個變量是Handler類型的,一個Looper對象可以對應多個Handler對象。

Message類

Message是消息的載體。Message設計成了Parelable類的派生類,表明Message可以通過binder來跨進程發送。

public final class Message implements Parcelable 

消息的類型

成員 類型 註解
what int 消息類別
when long 消息觸發時間
arg1 int 參數1
arg2 int 參數2
obj Object 消息內容
target Handler 消息相應的地方
callback Runnable 回調方法

obtain()

public static Message obtain() {//從消息池中返回一個新的Message實例,避免申請太多的Message
    synchronized (sPoolSync) {//消息池中有從消息池中取出
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

recycle()

public void recycle() {//回收消息
    if (isInUse()) { //判斷消息是否正在使用
        if (gCheckRecycle) { 
            throw new IllegalStateException("This message cannot be recycled because it is still in use.");
        }
        return;
    }
    recycleUnchecked();//回收沒有使用的消息
}

MessageQueue類

消息的構造

MessageQueue對象的構造是調用本地方法nativeInit()完成的。nativeInit()創建了一個本地NativeMessageQueue,它本質上是一個代理類。它把Java層的調用轉變爲nativeLooper類的函數調用,native層的Looper實現了一整套完整的消息處理機制。但是Java層的Looper類和native層的Looper類並沒有直接的關係。MessageQueue雖然使用了Native層的Looper類,但只用到了等待/喚醒機制,其餘的如消息隊列的實現還是在Java層。

消息的處理

Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //調用本地方法等待nextPollTimeoutMillis秒,-1表示永遠阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);
            //針對this對象同步,只要next方法沒退出,再調用本對象的任何方法都將導致調用線程掛起
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  忽略普通消息,查找下一條異步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        //如果還沒有處理這條消息的時間,計算等待時間
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;  //取消阻塞的標誌
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
                //如果退出標誌設置了,銷燬native對象 
                if (mQuitting) {
                    dispose();
                    return null;
                }
                //檢查是否安裝了idle handler
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            pendingIdleHandlerCount = 0;
            //如果又idle handler ,循環繼續,而不是阻塞
            nextPollTimeoutMillis = 0;
        }
    }

向MessageQueue發消息

使用的是enqueueMessage()方法:

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//如果消息的target爲NULL,拋出異常
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {//如果加入的是正在處理的消息對象,拋出異常
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        synchronized (this) {//用this對象同步
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;//p指向消息隊列頭
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg; //把消息插到隊列頭
                needWake = mBlocked;//這時如果處理線程阻塞了,則需要喚醒
            } else {
                //如果設置了“SyncBarrier”,只有插入了“異步消息”才需要喚醒
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                  //如果已經一條“異步消息”在隊列裏了,而且在本條消息前處理,而不需要喚醒
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            if (needWake) {//如果需要喚醒,則喚醒線程
                nativeWake(mPtr);
            }
        }
        return true;
    }

enqueueMessage()方法插入消息時根據時間來排序,時間早的插在前面。消息隊列的組織利用了Message類裏面的next指針形成一個從頭指向尾的單向鏈表。插入時計算是否需要喚醒處理。enqueueMessage()方法會盡量避免喚醒處理線程,只有插入了一條馬上要處理的消息,或者在暫停處理消息的情況下,有插入了“異步消息”的情況下才會去喚醒處理線程。其餘的情況都是把消息放到隊列的中部或尾部(時間未到)。如果前面還有消息沒處理,這條消息就更不急於處理了。

Hander類

構造Handler

構造一個Handler對象需要兩個參數,線程的Looper對象和消息的處理函數。Looper是必須的,構造方法不指定則使用當前線程的Looper對象。但是callback不是必須的,可以用callback實現對消息的集中處理,也可以把處理消息的callback方法放在消息對象中。

無參構造

public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
    //匿名類、內部類或本地類都必須申明爲static,否則會警告可能出現內存泄露
    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());
        }
    }
    mLooper = Looper.myLooper(); //從sThreadLocal中獲取Looper對象,下面Looper中的函數可以看到
    if (mLooper == null) {
        throw new RuntimeException("");
    }
    mQueue = mLooper.mQueue; //消息隊列,來自Looper對象
    mCallback = callback;  //回調方法
    mAsynchronous = async; //設置消息是否爲異步處理方式
}
//Looper類中的方法,見2.3
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

有參構造

public Handler(Looper looper) {
    this(looper, null, false);
}

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

send方法

public final boolean sendMessage(Message msg){  return sendMessageDelayed(msg, 0);  }
public final boolean sendEmptyMessage(int what){  return sendEmptyMessageDelayed(what, 0);  }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
        ……
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        ……
        return enqueueMessage(queue, msg, uptimeMillis);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
        MessageQueue queue = mQueue;
        ……
        return enqueueMessage(queue, msg, 0);
}

所有的send消息都調用了enqueueMessage()方法,最終調用了MessageQueue中的enqueueMessage()方法,就是把消息加入到了消息隊列,並指定執行的時間。可以看到下面queue.enqueueMessage()方法中除了msg,只有時間。上述一系列的send方法只是爲了方便使用,可以從方法名中看出各自的使用情景。

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

post方法

    public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    public final boolean postAtTime(Runnable r, long uptimeMillis){
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }
    public final boolean postDelayed(Runnable r, long delayMillis){
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    public final boolean postAtFrontOfQueue(Runnable r){
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

從代碼上看這些post方法的實現也是用send,只是多了Runnable對象,然後調用getPostMessage()方法獲取一個Message對象來發送。

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;//這裏callback被攜帶在message中,參見3.1
    return m;
}
private static Message getPostMessage(Runnable r, Object token) {
    Message m = Message.obtain();
    m.obj = token;
    m.callback = r;
    return m;
}

dispatchMessage()方法

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);//message中的callback
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {//Handler構造時,指定的callback 
                    return;
                }
            }
            handleMessage(msg);//Handler默認方法
        }
    }

從上面可以看到消息分發的優先級:

  1. Message的回調方法:message.callback;
  2. Handler的回調方法:Handler.mCallback.handleMessage(msg);
  3. Handler的默認方法:Handler.handleMessage(msg)。

dispatchMessage()方法的調用在Looper類中的loop()方法

public static void loop() {//見2.4
    for (;;) {
        ……
        msg.target.dispatchMessage(msg);//消息分發
      ……
        msg.recycleUnchecked();//消息回收
    }
}

流程圖

這裏寫圖片描述

參考文獻

[1] http://gityuan.com/2015/12/26/handler-message-framework/

[2] http://yuqiangqiang.com/2014/11/08/android基礎總結/9-android下的Handler機制/

[3] http://www.kancloud.cn/digest/androidfrom-0/144443

[4] https://developer.android.com/training/multiple-threads/communicate-ui.html

[5] https://blog.nikitaog.me/2014/10/11/android-looper-handler-handlerthread-i/

[6] http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html#!comments

[7] http://www.jianshu.com/p/02962454adf7

[8] http://www.feeyan.cn/?p=17

[9] http://anany.me/2015/04/12/handler/

[10] http://gityuan.com/2015/12/27/handler-message-native/#nativepollonce

[11] https://www.zhihu.com/question/34652589

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章