当前位置:文档之家› Android 消息机制分析

Android 消息机制分析

Android 消息机制分析
Android 消息机制分析

Android 消息机制分析

前言

本文说的Android消息机制主要是指Handler的运行机制。Handler机制主要可以让我们在子线程中处理耗时操作,然后在主线程中更新UI。

本文涉及到的类有

Handler

Message

Looper

MessageQueue

ActivityThread

Handler的运行原理

android消息机制.png

对于Handler的运行原理相信大家都很熟悉了,网络上面也有很多这样的文章。大致就是在主线程中创建Handler,然后通过Handler给MessageQueue发送Message,该MessageQueue是在程序启动的时候ActivityThread在Main方法中调用Looper.prepareMainLooper()创建的,一个线程只有一个Looper,一个MessageQueue,Looper通过ThreadLocal存储在当前的线程中。Looper会使用loop方法不断的从MessageQueue中取Message,通过Message的taget来把消息分发到相应的Handler中进行处理。

ActivityThread

android程序启动时会首先执行ActivityThread的main方法,我们先来看看代码:

public static final void main(String[] args) {

SamplingProfilerIntegration.start();

Process.setArgV0("");

Looper.prepareMainLooper();

if (sMainThreadHandler == null) {

sMainThreadHandler = new Handler();

}

ActivityThread thread = new ActivityThread();

thread.attach(false);

if (false) {

Looper.myLooper().setMessageLogging(new

LogPrinter(Log.DEBUG, "ActivityThread"));

}

Looper.loop();

if (Process.supportsProcesses()) {

throw new RuntimeException("Main thread loop unexpectedly exited");

}

thread.detach();

String name = (thread.mInitialApplication != null)

? thread.mInitialApplication.getPackageName()

: "";

Slog.i(TAG, "Main thread of " + name + " is now exiting");

}

可以看到在程序一启动就调用了Looper.prepareMainLooper方法和Looper.loop方法。那么我们就先来看看Looper这个类。

Looper类

Looper.png

以上是Looper类主要的方法和变量,我们可以看到它持有了一个自己的引用,一个MessageQueue和一个ThreadLocal对象(当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal)。我们先看prepareMainLooper方法:

public static final void prepareMainLooper() {

prepare();

setMainLooper(myLooper());

if (Process.supportsProcesses()) {

myLooper().mQueue.mQuitAllowed = false;

}

}

逻辑很简单,其实就是一些初始化操作,我们继续跟进去。

public static final void prepare() {

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

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

}

sThreadLocal.set(new Looper());

}

prepare方法首先通过sThreadLocal.get()方法得到当前线程的Looper对象,看是否为空,如果不为空说明已经创建了Looper对象,就抛出异常,从异常打印的语句我们也可以指定,每个线程只能有一个Looper。如果当前线程还没有Looper,那么我们就new一个Looper并且通过sThreadLocal.set方法将其保存起来。

private Looper() {

mQueue = new MessageQueue();

mRun = true;

mThread = Thread.currentThread();

}

Looper类的构造方法,可以看到标明是私有的,也就是说外部不能直接通过new Looper()的方式创建Looper对象。在构造方法中创建了一个MessageQueue,这下我们知道主线程中的Looper和MessageQueue在程序一启动的时候就创建好了,不需要我们手动去创建。

private synchronized static void setMainLooper(Looper looper) { mMainLooper = looper;

}

public synchronized static final Looper getMainLooper() {

return mMainLooper;

}

public static final Looper myLooper() {

return (Looper)sThreadLocal.get();

}

这三个方法都很简单,一起看看。我们开始说了Looper类持有了本身的引用mMainLooper,setMainLooper方法就是为这个变量赋值,这是一个私有方法,说明在外部并不能调用该方法对mMainLooper进行赋值。在prepareMainLooper 方法中调用它时传入的参数是myLooper方法的返回值,myLooper方法的返回值正好就是当前线程的Looper对象,也就是说在prepare方法中为当前线程创建了一个Looper对象并保存,接下里又通过mylooper方法把该对象取出来赋值给了mMainLooper。

public static final void loop() {

//得到当前线程的Looper对象

Looper me = myLooper();

//得到Looper对象对应的MessageQueue

MessageQueue queue = me.mQueue;

// Make sure the identity of this thread is that of the local process,

// and keep track of what that identity token actually is.

Binder.clearCallingIdentity();

final long ident = Binder.clearCallingIdentity();

while (true) {

//从MessageQueue中得到一条消息,如果没有消息则阻塞

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

//if (!me.mRun) {

// break;

//}

if (msg != null) {

if (msg.target == null)

{

// No target is a magic identifier for the quit message.

return;

}

if (me.mLogging!= null)

me.mLogging.println(

">>>>> Dispatching to " + msg.target + ""

+ msg.callback + ": " + msg.what

);

//回调Message中的Hanlder对象的dispatcheMessage 方法

msg.target.dispatchMessage(msg);

if (me.mLogging!= null) me.mLogging.println(

"<<<<< Finished to " + msg.target + ""

+ msg.callback);

// Make sure that during the course of dispatching the

// identity of the thread wasn't corrupted.

final long newIdent = Binder.clearCallingIdentity();

if (ident != newIdent) {

Log.wtf("Looper", "Thread identity changed from 0x"

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

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

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

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

}

msg.recycle();

}

}

}

接着来看loop方法,loop方法内部首先会得到当前线程的Looper对象,

然后与该Looper对象对应的MessageQueue,也就是在Looper构造方法中创建

的那个,然后通过while(true)不断的从消息队列中获取消息。经过一系列的判断,最终回调Message所对应的targe的dispatchMessage方法把消息分发出去。

Message类

Message.png

Message类其实类似于JavaBean,封装对象提供get,set方法。

public final class Message implements Parcelable {

/**

* User-defined message code so that the recipient can identify * what this message is about. Each {@link Handler} has its own name-space

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

* with other handlers.

*/

public int what;

/**

* arg1 and arg2 are lower-cost alternatives to using

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

* few integer values.

*/

public int arg1;

/**

* arg1 and arg2 are lower-cost alternatives to using

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

* few integer values.

*/

public int arg2;

/**

* An arbitrary object to send to the recipient. When using * {@link Messenger} to send the message across processes this can only

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

* implemented by the application). For other data transfer use * {@link #setData}.

*

*

Note that Parcelable objects here are not supported prior to

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

*/

public Object obj;

/**

* Optional Messenger where replies to this message can be sent. The

* semantics of exactly how this is used are up to the sender and * receiver.

*/

public Messenger replyTo;

/*package*/ long when;

/*package*/ Bundle data;

/*package*/ Handler target;

/*package*/ Runnable callback;

// sometimes we store linked lists of these things

/*package*/ Message next;

private static Object mPoolSync = new Object();

private static Message mPool;

private static int mPoolSize = 0;

private static final int MAX_POOL_SIZE = 10;

/**

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

* avoid allocating new objects in many cases.

*/

public static Message obtain() {

synchronized (mPoolSync) {

if (mPool != null) {

Message m = mPool;

mPool = m.next;

m.next = null;

mPoolSize--;

return m;

}

}

return new Message();

}

/**

* Same as {@link #obtain()}, but copies the values of an existing * message (including its target) into the new one.

* @param orig Original message to copy.

* @return A Message object from the global pool.

*/

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()}, but sets the value for the target member on the Message returned.

* @param h Handler to assign to the returned Message object's target member.

* @return A Message object from the global pool.

*/

public static Message obtain(Handler h) {

Message m = obtain();

m.target = h;

return m;

}

/**

* Same as {@link #obtain(Handler)}, but assigns a callback Runnable on

* the Message that is returned.

* @param h Handler to assign to the returned Message object's target member.

* @param callback Runnable that will execute when the message is handled.

* @return A Message object from the global pool.

*/

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

m.target = h;

m.callback = callback;

return m;

}

/**

* Same as {@link #obtain()}, but sets the values for both target and

* what members on the Message.

* @param h Value to assign to the target member.

* @param what Value to assign to the what member.

* @return A Message object from the global pool.

*/

public static Message obtain(Handler h, int what) {

Message m = obtain();

m.target = h;

m.what = what;

return m;

}

/**

* Same as {@link #obtain()}, but sets the values of the target, what, and obj

* members.

* @param h The target value to set.

* @param what The what value to set.

* @param obj The object method to set.

* @return A Message object from the global pool.

*/

public static Message obtain(Handler h, int what, Object obj) { Message m = obtain();

m.target = h;

m.what = what;

m.obj = obj;

return m;

}

/**

* Same as {@link #obtain()}, but sets the values of the target, what,

* arg1, and arg2 members.

*

* @param h The target value to set.

* @param what The what value to set.

* @param arg1 The arg1 value to set.

* @param arg2 The arg2 value to set.

* @return A Message object from the global pool.

*/

public static Message obtain(Handler h, int what, int arg1, int arg2) {

Message m = obtain();

m.target = h;

m.what = what;

m.arg1 = arg1;

m.arg2 = arg2;

return m;

}

/**

* Same as {@link #obtain()}, but sets the values of the target, what,

* arg1, arg2, and obj members.

*

* @param h The target value to set.

* @param what The what value to set.

* @param arg1 The arg1 value to set.

* @param arg2 The arg2 value to set.

* @param obj The obj value to set.

* @return A Message object from the global pool.

*/

public static Message obtain(Handler h, int what,

int arg1, int arg2, Object obj) {

Message m = obtain();

m.target = h;

m.what = what;

m.arg1 = arg1;

m.arg2 = arg2;

m.obj = obj;

return m;

}

/**

* Return a Message instance to the global pool. You MUST NOT touch

* the Message after calling this function -- it has effectively been

* freed.

*/

public void recycle() {

synchronized (mPoolSync) {

if (mPoolSize < MAX_POOL_SIZE) {

clearForRecycle();

next = mPool;

mPool = this;

mPoolSize++;

}

}

/**

* Make this message like o. Performs a shallow copy of the data field.

* Does not copy the linked list fields, nor the timestamp or * target/callback of the original message.

*/

public void copyFrom(Message o) {

this.what = o.what;

this.arg1 = o.arg1;

this.arg2 = o.arg2;

this.obj = o.obj;

this.replyTo = o.replyTo;

if (o.data != null) {

this.data = (Bundle) o.data.clone();

} else {

this.data = null;

}

}

/**

* Return the targeted delivery time of this message, in milliseconds.

*/

public long getWhen() {

return when;

}

public void setTarget(Handler target) {

this.target = target;

}

/**

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

* will receive this message. The object must implement

* {@link android.os.Handler#handleMessage(android.os.Message) * Handler.handleMessage()}. Each Handler has its own name-space for

* message codes, so you do not need to

* worry about yours conflicting with other handlers.

public Handler getTarget() {

return target;

}

/**

* Retrieve callback object that will execute when this message is handled.

* This object must implement Runnable. This is called by

* the target {@link Handler} that is receiving this Message to

* dispatch it. If

* not set, the message will be dispatched to the receiving Handler's

* {@link Handler#handleMessage(Message Handler.handleMessage())}.

*/

public Runnable getCallback() {

return callback;

}

/**

* Obtains a Bundle of arbitrary data associated with this

* event, lazily creating it if necessary. Set this value by calling

* {@link #setData(Bundle)}. Note that when transferring data across

* processes via {@link Messenger}, you will need to set your ClassLoader

* on the Bundle via {@link Bundle#setClassLoader(ClassLoader) * Bundle.setClassLoader()} so that it can instantiate your objects when

* you retrieve them.

* @see #peekData()

* @see #setData(Bundle)

*/

public Bundle getData() {

if (data == null) {

data = new Bundle();

}

return data;

}

* Like getData(), but does not lazily create the Bundle. A null * is returned if the Bundle does not already exist. See

* {@link #getData} for further information on this.

* @see #getData()

* @see #setData(Bundle)

*/

public Bundle peekData() {

return data;

}

/**

* Sets a Bundle of arbitrary data values. Use arg1 and arg1 members

* as a lower cost way to send a few simple integer values, if you can.

* @see #getData()

* @see #peekData()

*/

public void setData(Bundle data) {

this.data = data;

}

/**

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

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

*/

public void sendToTarget() {

target.sendMessage(this);

}

/*package*/ void clearForRecycle() {

what = 0;

arg1 = 0;

arg2 = 0;

obj = null;

replyTo = null;

when = 0;

target = null;

callback = null;

data = null;

}

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

*/

public Message() {

}

public String toString() {

return toString(SystemClock.uptimeMillis());

}

String toString(long now) {

StringBuilder b = new StringBuilder();

b.append("{ what=");

b.append(what);

b.append(" when=");

TimeUtils.formatDuration(when-now, b);

if (arg1 != 0) {

b.append(" arg1=");

b.append(arg1);

}

if (arg2 != 0) {

b.append(" arg2=");

b.append(arg2);

}

if (obj != null) {

b.append(" obj=");

b.append(obj);

}

b.append(" }");

return b.toString();

}

public static final Parcelable.Creator CREATOR

= new Parcelable.Creator() {

public Message createFromParcel(Parcel source) {

Message msg = Message.obtain();

msg.readFromParcel(source);

return msg;

}

public Message[] newArray(int size) {

return new Message[size];

}

};

public int describeContents() {

return 0;

}

public void writeToParcel(Parcel dest, int flags) {

if (callback != null) {

throw new RuntimeException(

"Can't marshal callbacks across processes.");

}

dest.writeInt(what);

dest.writeInt(arg1);

dest.writeInt(arg2);

if (obj != null) {

try {

Parcelable p = (Parcelable)obj;

dest.writeInt(1);

dest.writeParcelable(p, flags);

} catch (ClassCastException e) {

throw new RuntimeException(

"Can't marshal non-Parcelable objects across processes.");

}

} else {

dest.writeInt(0);

}

dest.writeLong(when);

dest.writeBundle(data);

Messenger.writeMessengerOrNullToParcel(replyTo, dest);

}

private final void readFromParcel(Parcel source) {

what = source.readInt();

arg1 = source.readInt();

arg2 = source.readInt();

if (source.readInt() != 0) {

obj = source.readParcelable(getClass().getClassLoader());

}

when = source.readLong();

data = source.readBundle();

replyTo = Messenger.readMessengerOrNullFromParcel(source);

}

}

Message类实在很简单,实现了Parcelable接口,Parcelable接口的介绍与serializable的区别请自行百度。在Message类中有一个消息池,池中的消息数最大为10,用mPoolSize变量来记录当前池中的消息数量,通过obtain和recycle方法来达到重复利用Message。在Message中要需要注意一下target

和callback这两个成员变量,前者是一个Handler,用来指明该Message是哪个Handler发出的,这样在从MessageQueue取出Message以后知道该回调哪个Handler的dispatchMessage方法。后者是一个Runnable对象,如果这个Runnable对象不为空的话,会优先执行这个对象的run方法。

MessageQueue

MessageQueue.png

MessageQueue很多都是native方法,用的比较多的就是上图列出的这两个。一个把消息入队,一个获取下一个Message。

final boolean enqueueMessage(Message msg, long when) {

if (msg.when != 0) {

throw new AndroidRuntimeException(msg

+ " This message is already in use.");

}

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

throw new RuntimeException("Main thread not allowed to quit");

}

final boolean needWake;

synchronized (this) {

if (mQuiting) {

RuntimeException e = new RuntimeException(

msg.target + " sending message to a Handler on a dead thread");

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

return false;

} else if (msg.target == null) {

mQuiting = true;

}

msg.when = when;

//Log.d("MessageQueue", "Enqueing: " + msg);

Message p = mMessages;

if (p == null || when == 0 || when < p.when) {

msg.next = p;

mMessages = msg;

needWake = mBlocked; // new head, might need to wake up

} else {

Message prev = null;

while (p != null && p.when <= when) {

prev = p;

p = p.next;

}

msg.next = prev.next;

prev.next = msg;

needWake = false; // still waiting on head, no need to wake up

}

}

if (needWake) {

nativeWake(mPtr);

}

return true;

}

final Message next() {

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

int nextPollTimeoutMillis = 0;

for (;;) {

if (nextPollTimeoutMillis != 0) {

Binder.flushPendingCommands();

}

nativePollOnce(mPtr, nextPollTimeoutMillis);

synchronized (this) {

// Try to retrieve the next message. 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 (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);

return msg;

} else {

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

}

} else {

nextPollTimeoutMillis = -1;

}

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

if (pendingIdleHandlerCount < 0) {

pendingIdleHandlerCount = mIdleHandlers.size();

}

if (pendingIdleHandlerCount == 0) {

// No idle handlers to run. Loop and wait some more.

mBlocked = true;

continue;

}

if (mPendingIdleHandlers == null) {

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

}

mPendingIdleHandlers =

mIdleHandlers.toArray(mPendingIdleHandlers);

}

// Run the idle handlers.

// We only ever reach this code block during the first iteration.

for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i];

mPendingIdleHandlers[i] = null; // release the reference to the handler

boolean keep = false;

try {

keep = idler.queueIdle();

} catch (Throwable t) {

Log.wtf("MessageQueue", "IdleHandler threw exception", t);

}

if (!keep) {

synchronized (this) {

mIdleHandlers.remove(idler);

}

}

}

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

pendingIdleHandlerCount = 0;

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

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

nextPollTimeoutMillis = 0;

}

}

代码我就不具体分析了,虽然MessageQueue是消息队列,但是这里是采用单链表来实现的。关于单链表的知识请看这。

Handler类

相关主题
文本预览
相关文档 最新文档