android原始碼解析--Message

傲慢的上校發表於2012-11-10

   馬上就要光棍節了,時間到此,這個光棍節前脫光是不太可能了,還是看程式碼吧,當你想請人看電影都請不到的時候,明天購物,今天看程式碼,到11點,看阿森納比賽,12點,買東西?現在的經濟狀況,真是負翁了。

好吧,進入正題吧,在上個週末,看了Handler原始碼解析MessageQueue原始碼解析Looper原始碼解析這三個原始碼,在handler提醒中,還有Message這個資訊載體類,今天來看下。

看下類定義:

Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.

While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.

定義一個包含任意型別的描述資料物件,此物件可以傳送給Handler。物件包含兩個額外的int欄位和一個額外的物件欄位,這樣可以使得在很多情況下不用做分配工作。儘管Message的構造器是公開的,但是獲取Message物件的最好方法是呼叫Message.obtain()或者Handler.obtainMessage(), 這樣是從一個可回收物件池中獲取Message物件。


public final class Message implements Parcelable 

Message類是個final類,就是說不能被繼承,同時Message類實現了Parcelable介面,我們知道android提供了一種新的型別:Parcel。本類被用作封裝資料的容器,封裝後的資料可以通過Intent或IPC傳遞。 除了基本型別以外,只有實現了Parcelable介面的類才能被放入Parcel中。

看一下全域性變數:

/**
     * User-defined message code so that the recipient can identify 
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
    public int what;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1; 

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;

    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     * 
     * <p>Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     */
    public Object obj;

    /**
     * Optional Messenger where replies to this message can be sent.  The
     * semantics of exactly how this is used are up to the sender and
     * receiver.
     */
    public Messenger replyTo;

    /** If set message is in use */
    /*package*/ static final int FLAG_IN_USE = 1;

    /** Flags reserved for future use (All are reserved for now) */
    /*package*/ static final int FLAGS_RESERVED = ~FLAG_IN_USE;

    /** Flags to clear in the copyFrom method */
    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAGS_RESERVED | FLAG_IN_USE;

    /*package*/ int flags;

    /*package*/ long when;
    
    /*package*/ Bundle data;
    
    /*package*/ Handler target;     
    
    /*package*/ Runnable callback;   
    
    // sometimes we store linked lists of these things
    /*package*/ Message next;

    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 10;

  1. what:使用者定義訊息程式碼以便收件人可以識別這是哪一個Message。每個Handler用它自己的名稱空間為訊息程式碼,所以您不需要擔心你的Handler與其他handler衝突。
  2. arg1、arg2:如果只是想向message內放一些整數值,可以使用arg1和arg2來代替setData方法。
  3. obj:傳送給接收器的任意物件。當使用Message物件線上程間傳遞訊息時,如果它包含一個Parcelable的結構類(不是由應用程式實現的類),此欄位必須為非空(non-null)。其他的資料傳輸則使用setData(Bundle)方法。注意Parcelable物件是從FROYO版本以後才開始支援的。
  4. replyTo:指明此message傳送到何處的可選Messenger物件。具體的使用方法由傳送者和接受者決定。
  5. FLAG_IN_USE:判斷Message是否在使用( default 包內可見
  6. FLAGS_RESERVED:留個將來使用??
  7. FLAGS_TO_CLEAR_ON_COPY_FROM:明確在copyFrom方法
  8. 其他引數都比較簡單,不詳述

下面看obtain方法:

/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

從全域性池中返回一個新的Message例項。在大多數情況下這樣可以避免分配新的物件。

在看它一系列的過載方法:

    /**
     * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
     * the Message that is returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @param callback Runnable that will execute when the message is handled.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values for both <em>target</em> and
     * <em>what</em> members on the Message.
     * @param h  Value to assign to the <em>target</em> member.
     * @param what  Value to assign to the <em>what</em> member.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, and <em>obj</em>
     * members.
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param obj  The <em>object</em> method to set.
     * @return  A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.obj = obj;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, 
     * <em>arg1</em>, and <em>arg2</em> members.
     * 
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param arg1  The <em>arg1</em> value to set.
     * @param arg2  The <em>arg2</em> value to set.
     * @return  A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what, int arg1, int arg2) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;

        return m;
    }

    /**
     * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, 
     * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
     * 
     * @param h  The <em>target</em> value to set.
     * @param what  The <em>what</em> value to set.
     * @param arg1  The <em>arg1</em> value to set.
     * @param arg2  The <em>arg2</em> value to set.
     * @param obj  The <em>obj</em> value to set.
     * @return  A Message object from the global pool.
     */
    public static Message obtain(Handler h, int what, 
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }

都是先呼叫obtain()方法,然後把獲取的Message例項加上各種引數。

    /**
     * Return a Message instance to the global pool.  You MUST NOT touch
     * the Message after calling this function -- it has effectively been
     * freed.
     */
    public void recycle() {
        clearForRecycle();

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

向全域性池中返回一個Message例項。一定不能在呼叫此函式後再使用Message——它會立即被釋放。

    /**
     * Make this message like o.  Performs a shallow copy of the data field.
     * Does not copy the linked list fields, nor the timestamp or
     * target/callback of the original message.
     */
    public void copyFrom(Message o) {
        this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
        this.what = o.what;
        this.arg1 = o.arg1;
        this.arg2 = o.arg2;
        this.obj = o.obj;
        this.replyTo = o.replyTo;

        if (o.data != null) {
            this.data = (Bundle) o.data.clone();
        } else {
            this.data = null;
        }
    }

使此message跟引數o相似。淺拷貝資料域。不拷貝源message的連結串列欄位,時間戳和目標/回撥。

/**
     * Return the targeted delivery time of this message, in milliseconds.
     */
    public long getWhen() {
        return when;
    }

設定一個任意資料值的Bundle物件。如果可以,使用arg1arg2域傳送一些整型值以減少消耗。

參考

         getData()

         peekData()


返回此訊息的傳輸時間,以毫秒為單位。

public void setTarget(Handler target) {
        this.target = target;
    }

設定目標handler(接收其訊息的Handler)。

   /**
     * Retrieve the a {@link android.os.Handler Handler} implementation that
     * will receive this message. The object must implement
     * {@link android.os.Handler#handleMessage(android.os.Message)
     * Handler.handleMessage()}. Each Handler has its own name-space for
     * message codes, so you do not need to
     * worry about yours conflicting with other handlers.
     */
    public Handler getTarget() {
        return target;
    }

獲取將接收此訊息的Handler物件。此物件必須要實現Handler.handleMessage()方法。每個handler各自包含自己的訊息程式碼,所以不用擔心自定義的訊息跟其他handlers有衝突。

 /**
     * Retrieve callback object that will execute when this message is handled.
     * This object must implement Runnable. This is called by
     * the <em>target</em> {@link Handler} that is receiving this Message to
     * dispatch it.  If
     * not set, the message will be dispatched to the receiving Handler's
     * {@link Handler#handleMessage(Message Handler.handleMessage())}.
     */
    public Runnable getCallback() {
        return callback;
    }

獲取回撥物件,此物件會在message處理時執行。此物件必須實現Runnable介面。回撥由接收此訊息並分發的目標handler呼叫。如果沒有設定回撥,此訊息會分發到接收handlerhandleMessage(Message)

/** 
     * Obtains a Bundle of arbitrary data associated with this
     * event, lazily creating it if necessary. Set this value by calling
     * {@link #setData(Bundle)}.  Note that when transferring data across
     * processes via {@link Messenger}, you will need to set your ClassLoader
     * on the Bundle via {@link Bundle#setClassLoader(ClassLoader)
     * Bundle.setClassLoader()} so that it can instantiate your objects when
     * you retrieve them.
     * @see #peekData()
     * @see #setData(Bundle)
     */
    public Bundle getData() {
        if (data == null) {
            data = new Bundle();
        }
        
        return data;
    }

獲取附加在此事件上的任意資料的Bundle物件,需要時延遲建立。通過呼叫setData(Bundle)來設定Bundle的值。需要注意的是,如果通過Messenger物件在程式間傳遞資料時,需要呼叫Bundle類的Bundle.setClassLoader()方法來設定ClassLoader,這樣當接收到訊息時可以例項化Bundle裡的物件。

         參考

                  peekData()

                  setData(Bundle)

    /** 
     * Like getData(), but does not lazily create the Bundle.  A null
     * is returned if the Bundle does not already exist.  See
     * {@link #getData} for further information on this.
     * @see #getData()
     * @see #setData(Bundle)
     */
    public Bundle peekData() {
        return data;
    }

getData()相似,但是並不延遲建立Bundle。如果Bundle物件不存在返回null。更多資訊見getData()

         參考

                   getData()

                   setData(Bundle)

    /**
     * Sets a Bundle of arbitrary data values. Use arg1 and arg1 members 
     * as a lower cost way to send a few simple integer values, if you can.
     * @see #getData() 
     * @see #peekData()
     */
    public void setData(Bundle data) {
        this.data = data;
    }

設定一個任意資料值的Bundle物件。如果可以,使用arg1arg2域傳送一些整型值以減少消耗。

參考

         getData()

         peekData()

    /**
     * Sends this Message to the Handler specified by {@link #getTarget}.
     * Throws a null pointer exception if this field has not been set.
     */
    public void sendToTarget() {
        target.sendMessage(this);
    }

Handler傳送此訊息,getTarget()方法可以獲取此Handler。如果這個欄位沒有設定會丟擲個空指標異常。

void clearForRecycle() {
        flags = 0;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        when = 0;
        target = null;
        callback = null;
        data = null;
    }

default方法,包內可見,清空所有資料。

   /*package*/ boolean isInUse() {
        return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
    }

    /*package*/ void markInUse() {
        flags |= FLAG_IN_USE;
    }

獲取Message是否在使用和標記為使用。

構造方法:

/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    }

跟推薦使用Message.obtain()方法。

toString方法:

public String toString() {
        return toString(SystemClock.uptimeMillis());
    }

    String toString(long now) {
        StringBuilder   b = new StringBuilder();
        
        b.append("{ what=");
        b.append(what);

        b.append(" when=");
        TimeUtils.formatDuration(when-now, b);

        if (arg1 != 0) {
            b.append(" arg1=");
            b.append(arg1);
        }

        if (arg2 != 0) {
            b.append(" arg2=");
            b.append(arg2);
        }

        if (obj != null) {
            b.append(" obj=");
            b.append(obj);
        }

        b.append(" }");
        
        return b.toString();
    }

public static final Parcelable.Creator<Message> CREATOR
            = new Parcelable.Creator<Message>() {
        public Message createFromParcel(Parcel source) {
            Message msg = Message.obtain();
            msg.readFromParcel(source);
            return msg;
        }
        
        public Message[] newArray(int size) {
            return new Message[size];
        }
    };

什麼作用?

public int describeContents() {
        return 0;
    }

描述了包含在Parcelable物件排列資訊中的特殊物件的型別。

返回值

         一個標誌位,表明Parcelable物件特殊物件型別集合的排列。

public void writeToParcel(Parcel dest, int flags) {
        if (callback != null) {
            throw new RuntimeException(
                "Can't marshal callbacks across processes.");
        }
        dest.writeInt(what);
        dest.writeInt(arg1);
        dest.writeInt(arg2);
        if (obj != null) {
            try {
                Parcelable p = (Parcelable)obj;
                dest.writeInt(1);
                dest.writeParcelable(p, flags);
            } catch (ClassCastException e) {
                throw new RuntimeException(
                    "Can't marshal non-Parcelable objects across processes.");
            }
        } else {
            dest.writeInt(0);
        }
        dest.writeLong(when);
        dest.writeBundle(data);
        Messenger.writeMessengerOrNullToParcel(replyTo, dest);
    }

    private final void readFromParcel(Parcel source) {
        what = source.readInt();
        arg1 = source.readInt();
        arg2 = source.readInt();
        if (source.readInt() != 0) {
            obj = source.readParcelable(getClass().getClassLoader());
        }
        when = source.readLong();
        data = source.readBundle();
        replyTo = Messenger.readMessengerOrNullFromParcel(source);
    }

將類的資料寫入外部提供的Parcel中和從Parcel中讀取資料。

好吧,這個是送給自己光棍節的禮物!!!


檢視更多原始碼內容:Android原始碼解析


相關文章