Android 消息机制(Java层)

来源:互联网 发布:源码解密 编辑:程序博客网 时间:2024/06/08 12:01

Android 消息机制(Java层)

(一)消息处理相关code review

// Looper.java

/**

  * Class used to run a message loop for athread.  Threads by default do

  * not have a message loop associated withthem; to create one, call

  * {@link #prepare} in the thread that is torun the loop, and then

  * {@link #loop} to have it process messagesuntil the loop is stopped.

  *

  * <p>Most interaction with a messageloop is through the

  * {@link Handler} class.

  *

  * <p>This is a typical example of theimplementation of a Looper thread,

  * using the separation of {@link #prepare}and {@link #loop} to create an

  * initial Handler to communicate with theLooper.

  *

  * <pre>

  * class LooperThread extends Thread {

  *     public Handler mHandler;

  *

  *     public void run() {

  *          Looper.prepare();

  *

  *         mHandler = new Handler() {

  *             public void handleMessage(Message msg) {

  *                  // process incoming messageshere

  *             }

  *         };

  *

  *         Looper.loop();

  *      }

  * }</pre>

  */

public class Looper {

    private static final String TAG ="Looper";

 

    // sThreadLocal.get() will return nullunless you've called prepare().

    static final ThreadLocal<Looper>sThreadLocal = new ThreadLocal<Looper>();

 

    final MessageQueue mQueue;

    final Thread mThread;

    volatile boolean mRun;

 

    private Printer mLogging = null;

    private static Looper mMainLooper =null;  // guarded by Looper.class

 

     /** Initialize the current thread as alooper.

      * This gives you a chance to createhandlers that then reference

      * this looper, before actually startingthe loop. Be sure to call

      * {@link #loop()} after calling thismethod, and end it by calling

      * {@link #quit()}.

      */

    public static void prepare() {

        if (sThreadLocal.get() != null) {

            throw newRuntimeException("Only one Looper may be created per thread");

        }

        sThreadLocal.set(new Looper());

    }

 

    /**

     * Initialize the current thread as a looper,marking it as an

     * application's main looper. The mainlooper 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;

    }

 

    private synchronized static voidsetMainLooper(Looper looper) {

        mMainLooper = looper;

    }

 

    /** Returns the application's main looper,which lives in the main thread of the application.

     */

    public synchronized static LoopergetMainLooper() {

        return mMainLooper;

    }

 

    /**

     * Run the message queue in this thread. Besure to call

     * {@link #quit()} to end the loop.

     */

    public static void loop() {

        Looper me = myLooper();

        if (me == null) {

            throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread.");

        }

       MessageQueue queue = me.mQueue;

       

        // Make sure the identity of thisthread is that of the local process,

        // and keep track of what that identitytoken actually is.

        Binder.clearCallingIdentity();

        final long ident =Binder.clearCallingIdentity();

       

        while (true) {

            Message msg = queue.next(); //might block

            if (msg != null) {

                if (msg.target == null) {

                    // No target is a magic identifierfor the quit message.

                    return;

                }

 

                long wallStart = 0;

                long threadStart = 0;

 

                // This must be in a localvariable, 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);

                    wallStart = SystemClock.currentTimeMicro();

                    threadStart =SystemClock.currentThreadTimeMicro();

                }

 

                msg.target.dispatchMessage(msg);/*调用Message中的成员变量target(即某个Handler派生类的实例)中的dispatchMessage()方法 -> 进而call到该Handler派生类中的handleMessage()*/

 

 

                if (logging != null) {

                    long wallTime =SystemClock.currentTimeMicro() - wallStart;

                    long threadTime =SystemClock.currentThreadTimeMicro() - threadStart;

 

                    logging.println("<<<<<Finished to " + msg.target + " " + msg.callback);

                    if (logging instanceofProfiler) {

                        ((Profiler)logging).profile(msg, wallStart, wallTime,

                                threadStart,threadTime);

                    }

                }

 

                // Make sure that during thecourse of dispatching the

                // identity of the threadwasn't corrupted.

                final long newIdent =Binder.clearCallingIdentity();

                if (ident != newIdent) {

                    Log.wtf(TAG, "Threadidentity 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();

            }

        }

    }

 

    /**

     * Return the Looper object associated withthe current thread.  Returns

     * null if the calling thread is notassociated with a Looper.

     */

    public static Looper myLooper() {

        return sThreadLocal.get();

    }

 

    /**

     * Control logging of messages as they areprocessed by this Looper.  If

     * enabled, a log message will be writtento <var>printer</var>

     * at the beginning and ending of eachmessage dispatch, identifying the

     * target Handler and message contents.

     *

     * @param printer A Printer object thatwill receive log messages, or

     * null to disable message logging.

     */

    public void setMessageLogging(Printerprinter) {

        mLogging = printer;

    }

   

    /**

     * Return the {@link MessageQueue} objectassociated with the current

     * thread. This must be called from a thread running a Looper, or a

     * NullPointerException will be thrown.

     */

    public static MessageQueue myQueue(){

        return myLooper().mQueue;

    }

 

    private Looper() {

        mQueue = new MessageQueue();//构造函数为私有,创建MessageQueue实例的地方

        mRun = true;

        mThread = Thread.currentThread();

    }

 

    public void quit() { // 线程退出

        Message msg = Message.obtain();

        // NOTE: By enqueueing directly intothe message queue, the

        // message is left with a nulltarget.  This is how we know it is

        // a quit message.

        mQueue.enqueueMessage(msg, 0);

    }

 

    /**

     * Return the Thread associated with thisLooper.

     */

    public Thread getThread() {

        return mThread;

    }

 

    /** @hide */

    public MessageQueue getQueue() {

        return mQueue;

    }

}

/**

  * Class used to run a message loop for athread.  Threads by default do

  * not have a message loop associated withthem; to create one, call

  * {@link #prepare} in the thread that is torun the loop, and then

  * {@link #loop} to have it process messagesuntil the loop is stopped.

  *

  * <p>Most interaction with a messageloop is through the

  * {@link Handler} class.

  *

  * <p>This is a typical example of theimplementation of a Looper thread,

  * using the separation of {@link #prepare}and {@link #loop} to create an

  * initial Handler to communicate with theLooper.

  *

  * <pre>

  * class LooperThread extends Thread {

  *     public Handler mHandler;

  *

  *     public void run() {

  *         Looper.prepare();

  *

  *         mHandler = new Handler() {

  *             public void handleMessage(Message msg) {

  *                  // process incoming messageshere

  *             }

  *         };

  *

  *         Looper.loop();

  *     }

  * }</pre>

  */

public class Looper {

    private static final String TAG ="Looper";

 

    // sThreadLocal.get() will return nullunless you've called prepare().

    static final ThreadLocal<Looper>sThreadLocal = new ThreadLocal<Looper>();

 

    final MessageQueue mQueue;

    final Thread mThread;

    volatile boolean mRun;

 

    private Printer mLogging = null;

    private static Looper mMainLooper =null;  // guarded by Looper.class

 

     /** Initialize the current thread as alooper.

      * This gives you a chance to createhandlers that then reference

      * this looper, before actually startingthe loop. Be sure to call

      * {@link #loop()} after calling thismethod, and end it by calling

      * {@link #quit()}.

      */

    public static void prepare() {

        if (sThreadLocal.get() != null) {

            throw new RuntimeException("Onlyone Looper may be created per thread");

        }

        sThreadLocal.set(new Looper());

    }

 

    /**

     * Initialize the current thread as alooper, marking it as an

     * application's main looper. The mainlooper 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;

    }

 

    private synchronized static voidsetMainLooper(Looper looper) {

        mMainLooper = looper;

    }

 

    /** Returns the application's main looper,which lives in the main thread of the application.

     */

    public synchronized static LoopergetMainLooper() {

        return mMainLooper;

    }

 

    /**

     * Run the message queue in this thread. Besure to call

     * {@link #quit()} to end the loop.

     */

    public static void loop() {

        Looper me = myLooper();

        if (me == null) {

            throw new RuntimeException("NoLooper; Looper.prepare() wasn't called on this thread.");

        }

        MessageQueue queue = me.mQueue;

       

        // Make sure the identity of thisthread is that of the local process,

        // and keep track of what that identitytoken actually is.

        Binder.clearCallingIdentity();

        final long ident =Binder.clearCallingIdentity();

       

        while (true) {

            Message msg = queue.next(); //might block

            if (msg != null) {

                if (msg.target == null) {

                    // No target is a magicidentifier for the quit message.

                    return;

                }

 

                long wallStart = 0;

                long threadStart = 0;

 

                // This must be in a localvariable, 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);

                    wallStart =SystemClock.currentTimeMicro();

                    threadStart =SystemClock.currentThreadTimeMicro();

                }

 

               msg.target.dispatchMessage(msg);/*调用Message中的成员变量target(即某个Handler派生类的实例)中的dispatchMessage()方法 -> 进而call到该Handler派生类中的handleMessage()*/

 

 

                if (logging != null) {

                    long wallTime =SystemClock.currentTimeMicro() - wallStart;

                    long threadTime =SystemClock.currentThreadTimeMicro() - threadStart;

 

                   logging.println("<<<<< Finished to " + msg.target+ " " + msg.callback);

                    if (logging instanceofProfiler) {

                        ((Profiler)logging).profile(msg, wallStart, wallTime,

                                threadStart,threadTime);

                    }

                }

 

                // Make sure that during thecourse of dispatching the

                // identity of the threadwasn't corrupted.

                final long newIdent =Binder.clearCallingIdentity();

                if (ident != newIdent) {

                    Log.wtf(TAG, "Thread identitychanged from 0x"

                            +Long.toHexString(ident) + " to 0x"

                            +Long.toHexString(newIdent) + " while dispatching to "

                            + msg.target.getClass().getName()+ " "

                            + msg.callback +" what=" + msg.what);

                }

               

                msg.recycle();

            }

        }

    }

 

    /**

     * Return the Looper object associated withthe current thread.  Returns

     * null if the calling thread is notassociated with a Looper.

     */

    public static Looper myLooper() {

        return sThreadLocal.get();

    }

 

    /**

     * Control logging of messages as they areprocessed by this Looper.  If

     * enabled, a log message will be writtento <var>printer</var>

     * at the beginning and ending of eachmessage dispatch, identifying the

     * target Handler and message contents.

     *

     *@param printer A Printer object that will receive log messages, or

     * null to disable message logging.

     */

    public void setMessageLogging(Printerprinter) {

        mLogging = printer;

    }

   

    /**

     * Return the {@link MessageQueue} objectassociated with the current

     * thread. This must be called from a thread running a Looper, or a

     * NullPointerException will be thrown.

     */

    public static MessageQueue myQueue() {

        return myLooper().mQueue;

    }

 

    private Looper() {

        mQueue = new MessageQueue(); //¹¹Ô캯ÊýΪ˽ÓУ¬´´½¨MessageQueueʵÀýµÄµØ·½

        mRun = true;

        mThread = Thread.currentThread();

    }

 

    public void quit() { // Ïß³ÌÍ˳ö

        Message msg = Message.obtain();

        // NOTE: By enqueueing directly into themessage queue, the

        // message is left with a nulltarget.  This is how we know it is

        // a quit message.

        mQueue.enqueueMessage(msg, 0);

    }

 

    /**

     * Return the Thread associated with thisLooper.

     */

    public Thread getThread() {

        return mThread;

    }

 

    /** @hide */

    public MessageQueue getQueue() {

        return mQueue;

    }

}



 

// Message.java

/**

 *

 * Defines a message containing a descriptionand arbitrary data object that can be

 * sent to a {@link Handler}.  This object contains two extra int fields andan

 * extra object field that allow you to not doallocations in many cases. 

 *

 * <p class="note">While theconstructor of Message is public, the best way to get

 * one of these is to call {@link #obtainMessage.obtain()} or one of the

 * {@link Handler#obtainMessageHandler.obtainMessage()} methods, which will pull

 * them from a pool of recycledobjects.</p>

 */

public final classMessage implements Parcelable {

    /**

     * User-defined message code so that therecipient can identify

     * what this message is about. Each {@linkHandler} has its own name-space

     * for message codes, so you do not need toworry about yours conflicting

     * with other handlers.

     */

    public int what;

 

    /**

     * arg1 and arg2 are lower-costalternatives to using

     * {@link #setData(Bundle) setData()} ifyou only need to store a

     * few integer values.

     */

    public int arg1;

 

    /**

     * arg1 and arg2 are lower-costalternatives to using

     * {@link #setData(Bundle) setData()} ifyou only need to store a

     * few integer values.

     */

    public int arg2;

 

    /**

     * Anarbitrary object to send to the recipient. When using

     * {@link Messenger} to send the messageacross processes this can only

     * be non-null if it contains a Parcelableof a framework class (not one

     * implemented by the application).   For other data transfer use

     * {@link #setData}.

     *

     * <p>Note that Parcelable objectshere are not supported prior to

     * the {@linkandroid.os.Build.VERSION_CODES#FROYO} release.

     */

    public Object obj;

 

    /**

     * Optional Messenger where replies to thismessage can be sent.  The

     * semantics of exactly how this is usedare 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 arereserved for now) */

    /*package*/ static final int FLAGS_RESERVED= ~FLAG_IN_USE;

 

    /** Flags to clear in the copyFrom method*/

    /*package*/ static final intFLAGS_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 thesethings

    /*package*/ Message next;

 

    private static final Object sPoolSync = newObject();

    private static Message sPool;

    private static int sPoolSize = 0;

 

    private static final int MAX_POOL_SIZE =10;

   

    /**

     * Return a new Message instance from theglobal pool. Allows us to

     * avoid allocating new objects in manycases.

     */

    public static Message obtain() {

        synchronized (sPoolSync) {

            if (sPool != null) {

                Message m = sPool;

                sPool = m.next;

                m.next = null;

                sPoolSize--;

                return m;

            }

        }

        return new Message();

    }

 

    /**

     * Same as {@link #obtain()}, but copiesthe values of an existing

     * message (including its target) into thenew one.

     * @param orig Original message to copy.

     * @return A Message object from the globalpool.

     */

    public static Message obtain(Message orig){

        Message m = obtain();

        m.what = orig.what;

        m.arg1 = orig.arg1;

        m.arg2 = orig.arg2;

        m.obj = orig.obj;

        m.replyTo = orig.replyTo;

        if (orig.data != null) {

            m.data = new Bundle(orig.data);

        }

        m.target = orig.target;

        m.callback = orig.callback;

 

        return m;

    }

 

    /**

     * Same as {@link #obtain(Handler)}, butassigns 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 willexecute when the message is handled.

     * @return A Message object from the globalpool.

     */

    public static Message obtain(Handler h,Runnable callback) {

        Message m = obtain();

        m.target = h;

        m.callback = callback;

 

        return m;

    }

    ......

    /**

     * Return the targeted delivery time ofthis message, in milliseconds.

     */

    public long getWhen() {

        return when;

    }

   

    public void setTarget(Handler target) {

        this.target = target;

    }

 

    /**

     * Retrieve the a {@link android.os.HandlerHandler} implementation that

     * will receive this message. The objectmust implement

     * {@link android.os.Handler#handleMessage(android.os.Message)

     * Handler.handleMessage()}. Each Handlerhas its own name-space for

     * message codes, so you do not need to

     * worry about yours conflicting with otherhandlers.

     */

    public Handler getTarget() {

        return target;

    }

 

    /**

     * Sends this Message to the Handlerspecified by {@link #getTarget}.

     * Throws a null pointer exception if thisfield has not been set.

     */

    public void sendToTarget() {

        target.sendMessage(this);

    }

 

    /*package*/ boolean isInUse() {

        return ((flags & FLAG_IN_USE) ==FLAG_IN_USE);

    }

 

    /*package*/ void markInUse() {

        flags |= FLAG_IN_USE;

    }

 

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

    */

    public Message() {

    }

}

 

// Handler.java

/**

 * A Handler allows you to send and process{@link Message} and Runnable

 * objects associated with a thread's {@linkMessageQueue}.  Each Handler

 * instance is associated with a single threadand that thread's message

 * queue. When you create a new Handler, it is bound to the thread /

 * message queue of the thread that is creatingit -- from that point on,

 * it will deliver messages and runnables tothat message queue and execute

 * them as they come out of the message queue.

 *

 * <p>There are two main uses for aHandler: (1) to schedule messages and

 * runnables to be executed as some point inthe future; and (2) to enqueue

 * an action to be performed on a differentthread than your own.

 *

 * <p>Scheduling messages is accomplishedwith the

 * {@link #post}, {@link #postAtTime(Runnable,long)},

 * {@link #postDelayed}, {@link#sendEmptyMessage},

 * {@link #sendMessage}, {@link#sendMessageAtTime}, and

 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow

 * you to enqueue Runnable objects to be calledby the message queue when

 * they are received; the<em>sendMessage</em> versions allow you to enqueue

 * a {@link Message} object containing a bundleof data that will be

 * processed by the Handler's {@link#handleMessage} method (requiring that

 * you implement a subclass of Handler).

 *

 * <p>When posting or sending to aHandler, you can either

 * allow the item to be processed as soon asthe message queue is ready

 * to do so, or specify a delay before it getsprocessed or absolute time for

 * it to be processed.  The latter two allow you to implementtimeouts,

 * ticks, and other timing-based behavior.

 *

 * <p>When a

 * process is created for your application, itsmain thread is dedicated to

 * running a message queue that takes care ofmanaging the top-level

 * application objects (activities, broadcastreceivers, etc) and any windows

 * they create. You can create your own threads, and communicate back with

 * the main application thread through aHandler.  This is done by calling

 * the same <em>post</em> or<em>sendMessage</em> methods as before, but from

 * your new thread.  The given Runnable or Message will then bescheduled

 * in the Handler's message queue and processedwhen appropriate.

 */

public class Handler{

    /*

     * Set this flag to true to detectanonymous, local or member classes

     * that extend this Handler class and thatare not static. These kind

     * of classes can potentially create leaks.

     */

    private static final booleanFIND_POTENTIAL_LEAKS = false;

    private static final String TAG ="Handler";

 

    /**

     * Callback interface you can use wheninstantiating a Handler to avoid

     * having to implement your own subclass ofHandler.

     */

    public interface Callback {

        public boolean handleMessage(Messagemsg);

    }

   

    /**

     * Subclasses must implement this to receivemessages.

     */

    public void handleMessage(Message msg) {

   // 通常,Handler的派生类必须实现此函数handleMessage(),进而进行消息相关处理

 } /** * Handle system messages here. */ 


    publicvoid dispatchMessage(Message msg){ 

    // thread -> Looper.java中的loop() -> Handler.java中的dispatchMessage() -> Handler的派生类中的handleMessage()

        if (msg.callback != null) {

            handleCallback(msg);

        } else {

            if (mCallback != null) {

                if(mCallback.handleMessage(msg)) {

                    return;

                }

            }

            handleMessage(msg);

        }

    }

 

    /**

     * Default constructor associates thishandler with the queue for the

     * current thread.

     *

     * If there isn't one, this handler won'tbe able to receive messages.

     */

    publicHandler() {

        if (FIND_POTENTIAL_LEAKS) {

            final Class<? extendsHandler> klass = getClass();

            if ((klass.isAnonymousClass() ||klass.isMemberClass() || klass.isLocalClass()) &&

                    (klass.getModifiers() &Modifier.STATIC) == 0) {

                Log.w(TAG, "The followingHandler class should be static or leaks might occur: " +

                    klass.getCanonicalName());

            }

        }

 

        mLooper= Looper.myLooper();

        if (mLooper == null) {

            throw new RuntimeException(

                "Can't create handlerinside thread that has not called Looper.prepare()");

        }

        mQueue= mLooper.mQueue;

        mCallback= null;

    }

 

    /**

     * Constructor associates this handler withthe queue for the

     * current thread and takes a callbackinterface in which you can handle

     * messages.

     */

    publicHandler(Callback callback) {

        if (FIND_POTENTIAL_LEAKS) {

            final Class<? extendsHandler> klass = getClass();

            if ((klass.isAnonymousClass() ||klass.isMemberClass() || klass.isLocalClass()) &&

                    (klass.getModifiers() &Modifier.STATIC) == 0) {

                Log.w(TAG, "The followingHandler class should be static or leaks might occur: " +

                    klass.getCanonicalName());

            }

        }

 

        mLooper= Looper.myLooper();

        if (mLooper == null) {

            throw new RuntimeException(

                "Can't create handlerinside thread that has not called Looper.prepare()");

        }

        mQueue= mLooper.mQueue;

        mCallback = callback;

    }

 

    /**

     * Use the provided queue instead of thedefault one.

     */

    public Handler(Looper looper) {

        mLooper= looper;

        mQueue = looper.mQueue;

        mCallback = null;

    }

 

    /**

     * Use the provided queue instead of thedefault one and take a callback

     * interface in which to handle messages.

     */

    public Handler(Looper looper, Callbackcallback) {

        mLooper = looper;

        mQueue = looper.mQueue;

        mCallback = callback;

    }

 

    /**

     * Returns a string representing the nameof the specified message.

     * The default implementation will eitherreturn the class name of the

     * message callback if any, or thehexadecimal representation of the

     * message "what" field.

     * 

     *@param message The message whose name is being queried

     */

    public String getMessageName(Messagemessage) {

        if (message.callback != null) {

            returnmessage.callback.getClass().getName();

        }

        return "0x" + Integer.toHexString(message.what);

    }

 

    /**

     * Returns a new {@link android.os.MessageMessage} from the global message pool. More efficient than

     * creating and allocating new instances.The retrieved message has its handler set to this instance (Message.target ==this).

     * If you don't want that facility, just call Message.obtain() instead.

     */

    public final Message obtainMessage()

    {

        return Message.obtain(this);

    }

 

    /**

     * Same as {@link #obtainMessage()}, exceptthat it also sets the what member of the returned Message.

     *

     * @param what Value to assign to thereturned Message.what field.

     * @return A Message from the globalmessage pool.

     */

    public final Message obtainMessage(intwhat)

    {

        return Message.obtain(this, what);

    }

   

    /**

     *

     * Same as {@link #obtainMessage()}, exceptthat it also sets the what and obj members

     * of the returned Message.

     *

     * @param what Value to assign to thereturned Message.what field.

     * @param obj Value to assign to thereturned Message.obj field.

     * @return A Message from the globalmessage pool.

     */

    public final Message obtainMessage(intwhat, Object obj)

    {

        return Message.obtain(this, what, obj);

    }

 

    /**

     *

     * Same as {@link #obtainMessage()}, exceptthat it also sets the what, arg1 and arg2 members of the returned

     * Message.

     * @param what Value to assign to thereturned Message.what field.

     * @param arg1 Value to assign to thereturned Message.arg1 field.

     * @param arg2 Value to assign to thereturned Message.arg2 field.

     * @return A Message from the globalmessage pool.

     */

    public final Message obtainMessage(intwhat, int arg1, int arg2)

    {

        return Message.obtain(this, what, arg1,arg2);

    }

   

    /**

     *

     * Same as {@link #obtainMessage()}, exceptthat it also sets the what, obj, arg1,and arg2 values on the

     * returned Message.

     * @param what Value to assign to thereturned Message.what field.

     * @param arg1 Value to assign to thereturned Message.arg1 field.

     * @param arg2 Value to assign to thereturned Message.arg2 field.

     * @param obj Value to assign to thereturned Message.obj field.

     * @return A Message from the globalmessage pool.

     */

    public final Message obtainMessage(intwhat, int arg1, int arg2, Object obj)

    {

        return Message.obtain(this, what, arg1,arg2, obj);

    }

 

    /**

     * Causes the Runnable r to be added to themessage queue.

     * The runnable will be run on the threadto which this handler is

     * attached.

     * 

     * @param r The Runnable that will beexecuted.

     *

     * @return Returns true if the Runnable wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.

     */

    public final boolean post(Runnable r)

    {

       return sendMessageDelayed(getPostMessage(r), 0);

    }

   

    /**

     * Causes the Runnable r to be added to themessage queue, to be run

     * at a specific time given by<var>uptimeMillis</var>.

     * <b>The time-base is {@linkandroid.os.SystemClock#uptimeMillis}.</b>

     * The runnable will be run on the threadto which this handler is attached.

     *

     * @param r The Runnable that will beexecuted.

     * @param uptimeMillis The absolute time atwhich the callback should run,

     *        using the {@link android.os.SystemClock#uptimeMillis} time-base.

     * 

     * @return Returns true if the Runnable wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.  Note that a

    *         result of true does notmean the Runnable will be processed -- if

     *        the looper is quit before the delivery time of the message

     *        occurs then the message will be dropped.

     */

    public final boolean postAtTime(Runnabler, long uptimeMillis)

    {

        returnsendMessageAtTime(getPostMessage(r), uptimeMillis);

    }

   

    /**

     * Causes the Runnable r to be added to themessage queue, to be run

     * at a specific time given by<var>uptimeMillis</var>.

     *<b>The time-base is {@linkandroid.os.SystemClock#uptimeMillis}.</b>

     * The runnable will be run on the threadto which this handler is attached.

     *

     * @param r The Runnable that will beexecuted.

     * @param uptimeMillis The absolute time atwhich the callback should run,

     *        using the {@link android.os.SystemClock#uptimeMillis} time-base.

     *

     * @return Returns true if the Runnable wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.  Note that a

     *        result of true does not mean the Runnable will be processed -- if

     *        the looper is quit before the delivery time of the message

     *        occurs then the message will be dropped.

     *        

     * @see android.os.SystemClock#uptimeMillis

     */

    public final boolean postAtTime(Runnabler, Object token, long uptimeMillis)

    {

        return sendMessageAtTime(getPostMessage(r,token), uptimeMillis);

    }

   

    /**

     * Causes the Runnable r to be added to themessage queue, to be run

     * after the specified amount of timeelapses.

     * The runnable will be run on the threadto which this handler

     * is attached.

     * 

     * @param r The Runnable that will beexecuted.

     * @param delayMillis The delay (inmilliseconds) until the Runnable

     *       will be executed.

     *       

     * @return Returns true if the Runnable wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.  Note that a

     *        result of true does not mean the Runnable will be processed --

     *        if the looper is quit before the delivery time of the message

     *        occurs then the message will be dropped.

     */

    public final boolean postDelayed(Runnabler, long delayMillis)

    {

        returnsendMessageDelayed(getPostMessage(r), delayMillis);

    }

   

    /**

     * Posts a message to an object thatimplements Runnable.

     * Causes the Runnable r to executed on thenext iteration through the

     * message queue. The runnable will be runon the thread to which this

     * handler is attached.

     * <b>This method is only for use invery special circumstances -- it

     * can easily starve the message queue,cause ordering problems, or have

     * other unexpected side-effects.</b>

     * 

     * @param r The Runnable that will beexecuted.

     *

     * @return Returns true if the message wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.

     */

    public final booleanpostAtFrontOfQueue(Runnable r)

    {

        returnsendMessageAtFrontOfQueue(getPostMessage(r));

    }

 

    /**

     * Remove any pending posts of Runnable rthat are in the message queue.

     */

    public final void removeCallbacks(Runnabler)

    {

        mQueue.removeMessages(this, r, null);

    }

 

    /**

     * Remove any pending posts of Runnable<var>r</var> with Object

     * <var>token</var> that are inthe message queue.  If<var>token</var> is null,

     * all callbacks will be removed.

     */

    public final void removeCallbacks(Runnabler, Object token)

    {

        mQueue.removeMessages(this, r, token);

    }

 

    /**

     * Pushes a message onto the end of themessage queue after all pending messages

     * before the current time. It will bereceived in {@link #handleMessage},

     * in the thread attached to this handler.

     * 

     * @return Returns true if the message wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.

     */

    public final boolean sendMessage(Messagemsg)

    {

        return sendMessageDelayed(msg, 0);

    }

 

    /**

     * Sends a Message containing only the whatvalue.

     * 

     * @return Returns true if the message wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.

     */

    public final boolean sendEmptyMessage(intwhat)

    {

        return sendEmptyMessageDelayed(what,0);

    }

 

    /**

     * Sends a Message containing only the whatvalue, to be delivered

     * after the specified amount of timeelapses.

     * @see#sendMessageDelayed(android.os.Message, long)

     *

     * @return Returns true if the message wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.

     */

    public final booleansendEmptyMessageDelayed(int what, long delayMillis) {

        Message msg = Message.obtain();

        msg.what = what;

        return sendMessageDelayed(msg,delayMillis);

    }

 

    /**

     * Sends a Message containing only the whatvalue, to be delivered

     * at a specific time.

     * @see #sendMessageAtTime(android.os.Message,long)

     * 

     * @return Returns true if the message wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.

     */

 

    public final booleansendEmptyMessageAtTime(int what, long uptimeMillis) {

        Message msg = Message.obtain();

        msg.what = what;

        return sendMessageAtTime(msg,uptimeMillis);

    }

 

    /**

     * Enqueue a message into the message queueafter all pending messages

     * before (current time + delayMillis). Youwill receive it in

     * {@link #handleMessage}, in the threadattached to this handler.

     * 

     * @return Returns true if the message wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, 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 final booleansendMessageDelayed(Message msg, long delayMillis)

    {

        if (delayMillis < 0) {

            delayMillis = 0;

        }

        return sendMessageAtTime(msg,SystemClock.uptimeMillis() + delayMillis);

    }

 

    /**

     * Enqueue a message into the message queueafter all pending messages

     * before the absolute time (in milliseconds)<var>uptimeMillis</var>.

     * <b>The time-base is {@linkandroid.os.SystemClock#uptimeMillis}.</b>

     * You will receive it in {@link#handleMessage}, in the thread attached

     * to this handler.

     *

     * @param uptimeMillis The absolute time atwhich the message should be

     *        delivered, using the

     *        {@link android.os.SystemClock#uptimeMillis} time-base.

     *        

     * @return Returns true if the message wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, 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 deliverytime of the message

     *        occurs then the message will be dropped.

     */

    public boolean sendMessageAtTime(Messagemsg, long uptimeMillis)

    {

        boolean sent = false;

        MessageQueue queue = mQueue;

        if (queue != null) {

            msg.target = this;

            sent = queue.enqueueMessage(msg,uptimeMillis);

        }

        else {

            RuntimeException e = newRuntimeException(

                this + " sendMessageAtTime()called with no mQueue");

            Log.w("Looper",e.getMessage(), e);

        }

        return sent;

    }

 

    /**

     * Enqueue a message at the front of themessage queue, to be processed on

     * the next iteration of the messageloop.  You will receive it in

     * {@link #handleMessage}, in the threadattached to this handler.

     * <b>This method is only for use invery special circumstances -- it

     * can easily starve the message queue,cause ordering problems, or have

     * other unexpected side-effects.</b>

     * 

     * @return Returns true if the message wassuccessfully placed in to the

     *        message queue.  Returns false onfailure, usually because the

     *        looper processing the message queue is exiting.

     */

    public final booleansendMessageAtFrontOfQueue(Message msg)

    {

        boolean sent = false;

        MessageQueue queue = mQueue;

        if (queue != null) {

            msg.target = this;

            sent = queue.enqueueMessage(msg,0);

        }

        else {

            RuntimeException e = newRuntimeException(

                this + "sendMessageAtTime() called with no mQueue");

            Log.w("Looper",e.getMessage(), e);

        }

        return sent;

    }

 

    /**

     * Remove any pending posts of messageswith code 'what' that are in the

     * message queue.

     */

    public final void removeMessages(int what){

        mQueue.removeMessages(this, what, null,true);

    }

 

    /**

     * Remove any pending posts of messageswith code 'what' and whose obj is

     * 'object' that are in the messagequeue.  If <var>token</var>is null,

     * all messages will be removed.

     */

    public final void removeMessages(int what,Object object) {

        mQueue.removeMessages(this, what,object, true);

    }

 

    /**

     * Remove any pending posts of callbacksand sent messages whose

     * <var>obj</var> is<var>token</var>.  If<var>token</var> is null,

     * all callbacks and messages will beremoved.

     */

    public final voidremoveCallbacksAndMessages(Object token) {

        mQueue.removeCallbacksAndMessages(this,token);

    }

 

    /**

     * Check if there are any pending posts ofmessages with code 'what' in

     * the message queue.

     */

    public final boolean hasMessages(int what){

        return mQueue.removeMessages(this,what, null, false);

    }

 

    /**

     * Check if there are any pending posts ofmessages with code 'what' and

     * whose obj is 'object' in the messagequeue.

     */

    public final boolean hasMessages(int what,Object object) {

        return mQueue.removeMessages(this,what, object, false);

    }

 

    // if we can get rid of this method, thehandler need not remember its loop

    // we could instead export a getMessageQueue()method...

    public final Looper getLooper() {

        return mLooper;

    }

 

    private final MessagegetPostMessage(Runnable r, Object token) {

        Message m = Message.obtain();

        m.obj = token;

        m.callback = r;

        return m;

    }

 

    private final void handleCallback(Messagemessage) {

        message.callback.run();

    }

 

    final MessageQueue mQueue;

    final Looper mLooper;

    final Callback mCallback;

    IMessenger mMessenger;

}

 

// MessageQueue.java

/**

 * Low-level class holding the list of messagesto be dispatched by a

 * {@link Looper}.  Messages are not added directly to aMessageQueue,

 * but rather through {@link Handler} objectsassociated with the Looper.

 *

 * <p>You can retrieve the MessageQueuefor the current thread with

 * {@link Looper#myQueue() Looper.myQueue()}.

 */

public classMessageQueue {

    Message mMessages;

    private final ArrayList<IdleHandler>mIdleHandlers = new ArrayList<IdleHandler>();

    private IdleHandler[] mPendingIdleHandlers;

    private boolean mQuiting;

    boolean mQuitAllowed = true;

 

    // Indicates whether next() is blockedwaiting in pollOnce() with a non-zero timeout.

    private boolean mBlocked;

 

    @SuppressWarnings("unused")

    private int mPtr; // used by native code

   

    private native void nativeInit();

    private native void nativeDestroy();

    private native void nativePollOnce(int ptr,int timeoutMillis);

    private native void nativeWake(int ptr);

 

    /**

     * Callback interface for discovering whena thread is going to block

     * waiting for more messages.

     */

    public static interface IdleHandler {

        /**

         * Called when the message queue hasrun out of messages and will now

         * wait for more.  Return true to keep your idle handler active,false

         * to have it removed.  This may be called if there are stillmessages

         * pending in the queue, but they areall scheduled to be dispatched

         * after the current time.

         */

        boolean queueIdle();

    }

 

    /**

     * Add a new {@link IdleHandler} to thismessage queue.  This may be

     * removed automatically for you byreturning false from

     * {@link IdleHandler#queueIdleIdleHandler.queueIdle()} when it is

     * invoked, or explicitly removing it with{@link #removeIdleHandler}.

     *

     * <p>This method is safe to callfrom any thread.

     *

     * @param handler The IdleHandler to beadded.

     */

    public final voidaddIdleHandler(IdleHandler handler) {

        if (handler == null) {

            throw newNullPointerException("Can't add a null IdleHandler");

        }

        synchronized (this) {

            mIdleHandlers.add(handler);

        }

    }

 

    /**

     * Remove an {@link IdleHandler} from thequeue that was previously added

     * with {@link #addIdleHandler}.  If the given object is not currently

     * in the idle list, nothing is done.

     *

     * @param handler The IdleHandler to beremoved.

     */

    public final voidremoveIdleHandler(IdleHandler handler) {

        synchronized (this) {

            mIdleHandlers.remove(handler);

        }

    }

   

   MessageQueue() {

        nativeInit();

    }

   

    @Override

    protected void finalize() throws Throwable{

        try {

            nativeDestroy();

        } finally {

            super.finalize();

        }

    }

 

    final Message next() {

        int pendingIdleHandlerCount = -1; // -1only during first iteration

        int nextPollTimeoutMillis = 0;

 

        for (;;) {

            if (nextPollTimeoutMillis != 0) {

                Binder.flushPendingCommands();

            }

            nativePollOnce(mPtr,nextPollTimeoutMillis);

 

            synchronized (this) {

                // Try to retrieve the nextmessage.  Return if found.

                final long now =SystemClock.uptimeMillis();

                final Message msg = mMessages;

                if (msg != null) {

                    final long when = msg.when;

                    if (now >= when) {

                        mBlocked = false;

                        mMessages = msg.next;

                        msg.next = null;

                        if (false)Log.v("MessageQueue", "Returning message: " + msg);

                        msg.markInUse();

                        return msg;

                    } else {

                        nextPollTimeoutMillis =(int) Math.min(when - now, Integer.MAX_VALUE);

                    }

                } else {

                    nextPollTimeoutMillis = -1;

                }

 

                // If first time, then get thenumber of idlers to run.

                if (pendingIdleHandlerCount< 0) {

                    pendingIdleHandlerCount = mIdleHandlers.size();

                }

                if (pendingIdleHandlerCount ==0) {

                    // No idle handlers torun.  Loop and wait some more.

                    mBlocked = true;

                    continue;

                }

 

                if (mPendingIdleHandlers == null) {

                    mPendingIdleHandlers = newIdleHandler[Math.max(pendingIdleHandlerCount, 4)];

                }

                mPendingIdleHandlers =mIdleHandlers.toArray(mPendingIdleHandlers);

            }

 

            // Run the idle handlers.

            // We only ever reach this codeblock 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("MessageQueue","IdleHandler threw exception", t);

                }

 

                if (!keep) {

                    synchronized (this) {

                       mIdleHandlers.remove(idler);

                    }

                }

            }

 

            // Reset the idle handler count to 0so we do not run them again.

            pendingIdleHandlerCount = 0;

 

            // While calling an idle handler, anew message could have been delivered

            // so go back and look again for apending message without waiting.

            nextPollTimeoutMillis = 0;

        }

    }

 

    final boolean enqueueMessage(Message msg, longwhen) {

        if (msg.isInUse()) {

            throw new AndroidRuntimeException(msg

                    + " This message isalready in use.");

        }

        if (msg.target == null &&!mQuitAllowed) {

            throw newRuntimeException("Main thread not allowed to quit");

        }

        final boolean needWake;

        synchronized (this) {

            if (mQuiting) {

                RuntimeException e = newRuntimeException(

                    msg.target + " sendingmessage 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; // newhead, 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; // stillwaiting on head, no need to wake up

            }

        }

        if (needWake) {

            nativeWake(mPtr);

        }

        return true;

    }

 

    final boolean removeMessages(Handler h, intwhat, Object object,

            boolean doRemove) {

        synchronized (this) {

            Message p = mMessages;

            boolean found = false;

 

            // Remove all messages at front.

            while (p != null &&p.target == h && p.what == what

                   && (object == null|| p.obj == object)) {

                if (!doRemove) return true;

                found = true;

                Message n = p.next;

                mMessages = n;

                p.recycle();

                p = n;

            }

 

            // Remove all messages after front.

            while (p != null) {

                Message n = p.next;

                if (n != null) {

                    if (n.target == h&& n.what == what

                        && (object ==null || n.obj == object)) {

                        if (!doRemove) returntrue;

                        found = true;

                        Message nn = n.next;

                        n.recycle();

                        p.next = nn;

                        continue;

                    }

                }

                p = n;

            }

           

            return found;

        }

    }

 

    final void removeMessages(Handler h,Runnable r, Object object) {

        if (r == null) {

            return;

        }

 

        synchronized (this) {

            Message p = mMessages;

 

            // Remove all messages at front.

            while (p != null &&p.target == h && p.callback == r

                   && (object == null|| p.obj == object)) {

                Message n = p.next;

                mMessages = n;

                p.recycle();

                p = n;

            }

 

            // Remove all messages after front.

            while (p != null) {

                Message n = p.next;

                if (n != null) {

                    if (n.target == h&& n.callback == r

                        && (object ==null || n.obj == object)) {

                        Message nn = n.next;

                        n.recycle();

                        p.next = nn;

                        continue;

                    }

                }

                p = n;

            }

        }

    }

}

 

//HandlerThread.java

// Google公司提供的使用范例(Thread和Looper的结合)

public classHandlerThread extends Thread {

    int mPriority;

    int mTid = -1;

    Looper mLooper;

 

    public HandlerThread(String name) {

        super(name);

        mPriority =Process.THREAD_PRIORITY_DEFAULT;

    }

   

    /**

     * Constructs a HandlerThread.

     * @param name

     * @param priority The priority to run thethread at. The value supplied must be from

     * {@link android.os.Process} and not fromjava.lang.Thread.

     */

    public HandlerThread(String name, intpriority) {

        super(name);

        mPriority = priority;

    }

   

    /**

     * Call back method that can be explicitlyover ridden if needed to execute some

     * setup before Looper loops.

     */

    protected void onLooperPrepared() {

    }

 

    public void run() {

        mTid = Process.myTid();

        Looper.prepare();

        synchronized (this) {

            mLooper= Looper.myLooper();

            notifyAll();

        }

        Process.setThreadPriority(mPriority);

        onLooperPrepared();

        Looper.loop();

        mTid = -1;

    }

   

    /**

     * This method returns the Looperassociated with this thread. If this thread not been started

     * or for any reason is isAlive() returnsfalse, this method will return null. If this thread

     * has been started, this method will blockuntil the looper has been initialized. 

     * @return The looper.

     */

    public Looper getLooper() {

        if (!isAlive()) {

            return null;

        }

       

        // If the thread has been started, waituntil the looper has been created.

        synchronized (this) {

            while (isAlive() && mLooper== null) {

                try {

                    wait();

                } catch (InterruptedExceptione) {

                }

            }

        }

        return mLooper;

    }

   

    /**

     * Ask the currently running looper toquit.  If the thread has not

     * been started or has finished (that is if{@link #getLooper} returns

     * null), then false is returned.  Otherwise the looper is asked to

     * quit and true is returned.

     */

    public boolean quit() {

        Looper looper = getLooper();

        if (looper != null) {

            looper.quit();

            return true;

        }

        return false;

    }

   

    /**

     * Returns the identifier of this thread.See Process.myTid().

     */

    public int getThreadId() {

        return mTid;

    }

}

(二)用法小结

  • Looper的构造函数为private,所以外界只可以通过Looper的一些static函数get 到Looper实例,且Looper的构造函数中会创建一个消息队列(MessageQueue);
  • 起初,Looper必须与某个Thread关联上(该Thread的线程函数负责call到Looper的loop(),而loop()是个while循环,不断轮询Looper内部的消息队列,并对各个消息进行dispatch);
  • Message中的成员变量Handler target,用于保存某个Handler派生类,使得Looper中loop()函数才可以将消息派送到具体的某个Handler;
  • 通过Handler的obtainMessage()或者 Message的obtain()可以获取到Message实例;
  • 通过call Handler中的sendMessage()可以将该Message实例发送至Looper中消息队列(MessageQueue实例)中。

(三)总结

       以上是作者在分析代码过程中作的简单记录,算是作个笔记,希望对有需要的朋友有所帮助。另外,推荐一篇文章:http://www.cyqdata.com/android/article-detail-36658

||  ==========  ||
code目录:\andorid_source-4.0.3\frameworks\base\core\java\android\os
Looper.java
Message.java
MessageQueue.java
Handler.java
HandlerThread.java
||  ========== ||