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
|| ========== ||