1. 程式人生 > >handler訊息機制原始碼級深入全解析

handler訊息機制原始碼級深入全解析

首先我們來看看Handler更新UI執行緒一般使用

首先要進行Handler 申明,複寫handleMessage方法( 放在主執行緒中)
private Handler handler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            // TODO 接收訊息並且去更新UI執行緒上的控制元件內容
            if (msg.what == UPDATE) {
                // 更新介面上的textview
                tv.setText(String.valueOf(msg.obj));
            }
            super.handleMessage(msg);
        }
    };
子執行緒傳送Message給ui執行緒表示自己任務已經執行完成,主執行緒可以做相應的操作了。
new Thread() {
            @Override
            public void run() {
                // TODO 子執行緒中通過handler傳送訊息給handler接收,由handler去更新TextView的值
                try {
                       //do something

                        Message msg = new Message();
                        msg.what = UPDATE;                  
                        msg.obj = "更新後的值" ;
                        handler.sendMessage(msg);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();

我們通過畫圖來了大體解一下handler的訊息機制

handler訊息機制圖解
先看左側我們通過handler來發訊息,這個實在子執行緒中的。handler可以建立多個,假設我們建立兩個handler為handler1和handler2.每個handler都可以向訊息佇列發訊息。handler1傳送訊息後到訊息佇列,假設訊息為msg1.handler2也會發送訊息排在訊息佇列的msg。但是訊息佇列是唯一的,訊息佇列會按照時間排序。訊息佇列依次從訊息佇列取訊息。之後通過輪詢器來取出訊息,輪詢器實在主執行緒中的。每個訊息通過target屬性來找到自己的訊息。然後再呼叫handleMessage()方法中處理結果。

下面我們通過四個類來將handler訊息機制弄清楚。Message,MesageQueue,Handler,Looper。

Message

  1, new Message()
  2, Message.obtain();
  3, handler.obtainMessage();

MesageQueue

  存放Message的容器,單鏈表
       預設:msg的when都是0  後加的放到最前面
            如果when不等於0  按when的值大小進入排序:when的值小的在佇列的前面

Handler

  訊息處理類
  handleMessage(Message msg)方法,覆蓋此方法,做ui的處理

Looper

  輪詢器 : 輪詢取訊息,linux底層管道通訊

Message類

 **//代表message值是什麼** 
    public int what;

    public int arg1; 


    public int arg2;

    **//如果資訊較多用Obj來作為資訊的載體。**
    public Object obj;


    public Messenger replyTo;

    **//  /*package*/為私有的訪問許可權並不對外提供。**

    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    /*package*/ int flags;
    **//發訊息的時間,預設情況下是0。寫1000即為等待1000毫秒中再發**
    /*package*/ long when;  
    /*package*/ Bundle data;
    **//target也就是用來區分message的**
    /*package*/ Handler target;     
    **//一個回撥介面**
    /*package*/ Runnable callback;   
    **//next其實就是一個單鏈表**
    /*package*/ Message next;

我們來看一下Message.obtain();中程式碼

//為了滿足語法來new Object();
 private static final Object sPoolSync = new Object();
 private static Message sPool;
 public static Message obtain() {

            synchronized (sPoolSync) {
            //sPool是一個訊息池,上面定義了,也就還是一個訊息。
                if (sPool != null) {
                    //從訊息池中取訊息。
                    Message m = sPool;
                    //然後再指向下一個。
                    sPool = m.next;
                    m.next = null;
                    sPoolSize--;
                    return m;
                }
            }
            //如果訊息池中沒有訊息也就是直接建立了一個新的訊息。
            return new Message();
        }
我們再來看一下clearForRecycle()這個方法。
//這個方法將所有的屬性進行了清零。
void clearForRecycle() {
        flags = 0;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        when = 0;
        target = null;
        callback = null;
        data = null;
    }
    這也就是我們在如下程式碼中常犯的錯誤的解釋:
//將target=null。msg.what也就不知道到底是哪個target了。發訊息也就會報錯了。
Message msg = Message.obtain(); 
    msg.what = 1;
    msg.what = 2;
為什麼會呼叫clearForRecycle()這個方法呢?我們看一下下面的方法。也就是在recycle()這個方法中呼叫了clearForRecycle()方法。
 public void recycle() {
        clearForRecycle();

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                //將靜態變數sPool賦值給了next
                next = sPool;
                 //這個又指向了sPool
                sPool = this;
                sPoolSize++;
            }
        }
    }

通過上面的方法中的兩句、

     //將靜態變數sPool賦值給了next
                next = sPool;
                 //這個又指向了sPool
                sPool = this;

並且結合obtain()中的

     sPool = m.next;
     m.next = null;
我們就可以將兩個訊息重用為一個訊息了。綜上 Message類中的方法都跟這個Obtain()方法一樣。將訊息重用。
ActivityThread執行main();
    //1,
    Looper.prepareMainLooper();

    // 2,
    public static final void prepareMainLooper() {
        //3
        prepare();

        setMainLooper(myLooper());
        if (Process.supportsProcesses()) {
            myLooper().mQueue.mQuitAllowed = false;
        }
    }
  // 4
  public static final void prepare() {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //5,,呼叫的時候Looper.myLooper()-->
    sThreadLocal.set(new Looper());
}
// 6,在handler的預設建構函式中去呼叫取值
public static final Looper myLooper() {
    return (Looper)sThreadLocal.get();
}
Handler()的建構函式
public Handler() {
        //輪詢器的初始化,主執行緒
        mLooper = Looper.myLooper();

        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 從輪詢器中獲取訊息佇列
        mQueue = mLooper.mQueue;

        //回撥初始化為null
        mCallback = null;
    }
通過上面我們需要看兩個程式碼
1,  Looper.myLooper();

     public static final Looper myLooper() {
        //從sThreadLocal取值 ,賦值看 上面##### ActivityThread執行main()
        //ThreadLocal 執行緒變數。開啟多個執行緒的時候,為每個執行緒設定一個值。當前執行緒再去取值的時候,他會取出自己執行緒的值。而不會取到別的執行緒設定的值。
        return (Looper)sThreadLocal.get();
    }
2, mQueue = mLooper.mQueue;//獲取輪詢器中的訊息佇列
Handler傳送Message

1,handler.sendMessage(msg);//傳送訊息

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

2,sendMessageDelayed(msg, 0);

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

3,sendMessageAtTime()

       public boolean sendMessageAtTime(Message msg, long uptimeMillis)
        {
            boolean sent = false;
            //獲取訊息佇列,mQueue的賦值請看Handler的預設建構函式
            MessageQueue queue = mQueue;
            if (queue != null) {
                //把自己的handler和Message繫結,this就是Handler
                msg.target = this;
                //把msg放到訊息佇列並且完成排序(按時間和順序)
                sent = queue.enqueueMessage(msg, uptimeMillis);
            }
            else {
                RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
            }
            return sent;
        }

4,sent = queue.enqueueMessage(msg, uptimeMillis);

    訊息佇列裡enqueueMessage方法,
   1,放訊息
   2,排序(按訊息的時間排序,如果時間一樣,後加的在前面
    final boolean enqueueMessage(Message msg, long when) {
        if (msg.when != 0) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        final boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }
Looper取訊息
 在應用程式啟動之前
 ActivityThread的main已經執行好,並且開始等待接收訊息
 呼叫 Looper.loop();//輪詢取訊息

1,Looper的loop方法

    while (true) {
            //訊息佇列
            Message msg = queue.next(); // might block
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                //找不到對應的handler
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                //處理訊息
                msg.target.dispatchMessage(msg);
                if (me.mLogging!= null) me.mLogging.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("Looper", "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);
                }
                //4,
                msg.recycle();
            }
        }
    }

2, msg.target.dispatchMessage(msg);
呼叫Handler的dispatchMessage分發訊息

 public void dispatchMessage(Message msg) {
    if (msg.callback != null) {// runOnUiThread
        //2.5

        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //3handleMessage
        handleMessage(msg);
    }
}

2.5 handleCallback(msg); 使用者呼叫了runOnUiThread方法呼叫

a,
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}
b,
mHandler.post(action);

public final boolean post(Runnable r)
{
   c,
   return  sendMessageDelayed(getPostMessage(r), 0);
}
 d,

 private final Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

e,
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    //把handler發出去  msg.callback= Runnable
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

//////////上面abcde是通過runOnUiThread方法傳送訊息的邏輯

取訊息
handleCallback(msg);
private final void handleCallback(Message message) {
message.callback.run();//runOnUiThread裡寫的功能,在主執行緒執行
}

3, 在主執行緒中呼叫Handler的handleMessage(msg)使用者覆蓋的方法處理訊息
處理ui相關的操作
4, msg.recycle();
msg的回收

  public void recycle() {
    synchronized (mPoolSync) {
        if (mPoolSize < MAX_POOL_SIZE) {
            clearForRecycle();

            next = mPool;
            mPool = this;
        }
    }
}