android handlerthread 通知机制

自从涉足android之日起,越来越觉得android深不可测,每个模块,每种机制都能让你琢磨很一段时间,内部的封装实在精深。早就想做handlerthread进程的研究,写点东西出来,可都半途而废,终于还是抽空写了点早就应该写的东西,这方面的资料也还算不少,但都不是很全面,深入理解Android消息处理机制对于应用程序开发非常重要,也可以让你对线程同步有更加深刻的认识。下面将全面分析android中的消息处理机制,以供大家学习参考,也方便自己以后的回顾。

本文解析Android如何利用Handler/Thread/Looper以及MessageQueue来实现消息机制的内部实现,了解了它的内部实现机理,对我们以后分析android模块有莫大的帮助。要了解handlerthread的工作过程,就要先分析looper,handler和Thread这三个类在消息处理机制中的作用,从名字就可以猜出个大概来,handlerThread自然会与Thread和Handler有关联,至于looper,那是一个消息处理循环主体。

其实HandlerThread就是Thread的子类,一个线程,在线程中进行looper的消息分发/处理循环,而handler主要用来传递消息,并被调用来处理消息,所以要清楚分析android的消息处理机制,要从三个方面来分别做分析:

1)android消息处理机制中相关类代码解析

2)android消息处理机制中各类之间关系

3)以线程的角度分析,使用该机制的技巧和注意的问题

为了方便参考代码和理清后面的关系,我以代码中重要部分穿插解释来进行说明。

一,相关类代码解析

(1)looper

所在文件looper.java, looper: 消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper,类的定义如下所示:

public class Looper {

//与打印调试信息相关的变量。

private static final boolean DEBUG = false;

private static final boolean localLOGV =DEBUG ? Config.LOGD : Config.LOGV;

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

//线程本地存储功能的封装,一个线程内有一个内部存储空间,线程相关的东西就存储 //到,这个线程的存储空间中,就不用放在堆上而进行同步操作了。

private static final ThreadLocal sThreadLocal = new ThreadLocal();

// MessageQueue,消息队列,就是存放消息的地方,handler就是往这里添加消息的

final MessageQueue mQueue;

volatile boolean mRun;

//设置looper的那个线程,其实就是当前线程

Thread mThread;

private Printer mLogging = null;

//代表一个UI Process的主线程

private static Looper mMainLooper = null;

/** 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()}.

*/

//这里的Prepare函数就是设置looper的函数,一个线程只能设一个looper,而且要 //注意looper类的构造函数是私有函数,looper外的类就是通过这个函数来实例化 //looper对象的

public static final 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 alooper, marking it as an application's main

*looper. The main looper for your application is created by the Androidenvironment,

*so you should never need to call this function yourself.

* {@link #prepare()}

*/

/*由framework设置的UI程序的主消息循环,注意,这个主消息循环是不会主动退出的提个醒,注意这里的mylooper()函数,后面有用到*/

public static final voidprepareMainLooper() {

prepare();

setMainLooper(myLooper());

if (Process.supportsProcesses()) {

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 final LoopergetMainLooper() {

return mMainLooper;

}

/**

*Run the message queue in this thread. Be sure to call

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

*/

/* 这个函数尤为重要,是一个无限循环体,不断从MessageQueue中取得消息,并分发处理,如果消息队列中没有消息了,它会调用wait()函数阻塞起来 */

public static final void loop() {

//从该线程中取looper对象,这个函数会频繁用到

Looper me = myLooper();

MessageQueue queue = me.mQueue;

while (true) {

Message msg = queue.next(); //取消息队列中的一个消息,可能会阻塞

//if (!me.mRun) {

//是否需要退出?

// break;

//}

if (msg != null) {

if (msg.target == null) {

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

return;

}

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

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

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

);

msg.target.dispatchMessage(msg);/*消息派遣函数,调用handler的一个消息派遣函数,用来处理消息,注意:这里不一定会用到handler的消息处理函数,在handler的部分会有介绍*/

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

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

+ msg.callback);

msg.recycle(); //过时消息的回收

}

}

}

/**

* Return the Looper object associated withthe current thread. Returns

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

*/

//就是这个函数,返回和线程相关的looper

public static final Looper myLooper() {

return (Looper)sThreadLocal.get();

}

......

/**

* 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 final MessageQueue myQueue(){

return myLooper().mQueue;

}

/*建一个新的looper对象,内部分配一个消息队列,设置mRun为true,注意这是私有函数,外界无法创建looper对象的原因*/

private Looper() {

mQueue = new 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;

}

......

......

}

(2)handler

所在文件handler.java,handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等,定义如下:

class Handler{

..........

//handler默认构造函数

public Handler() {

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());

}

}

//再次用到该函数,获取本线程的looper对象

mLooper = Looper.myLooper();

if (mLooper == null) {

throw new RuntimeException(

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

}

/*这里是消息通知机制的妙处所在,也是looper和handler的密切联系所在,这样他们就共用一个 Queue了,有木有!这样的话,handler就有理由添加消息到共用队列里了*/

mQueue = mLooper.mQueue;

mCallback = null;

}

/*handler有四个构造函数,上面的是无参构造函数,looper可以是线程自己的也可以由你来提供*/

public Handler(Looperlooper) {

mLooper = looper;

mQueue = looper.mQueue;

mCallback = null;

}

public Handler(Looper looper,Callback callback) {

mLooper = looper;

mQueue = looper.mQueue;

mCallback = callback;

}

//消息发送函数,调用了内部的一个sendMessageDelayed

public final boolean sendMessage(Messagemsg)

{

return sendMessageDelayed(msg, 0);

}

//调用sendMessageAtTime,这里只是进行一次相对时间和绝对时间的转换

public final booleansendMessageDelayed(Message msg, long delayMillis)

{

if (delayMillis < 0) {

delayMillis = 0;

}

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

}

public booleansendMessageAtTime(Message msg, long uptimeMillis)

{

boolean sent = false;

MessageQueue queue = mQueue;

if (queue != null) {

/*把消息的target设置为自己,然后加入到消息队列中,所以当looper类中的loop()函数调用msg.target.dispatchmessage(),就是是调用这个handler的dispatchmessage()*/

msg.target = this;

sent = queue.enqueueMessage(msg,uptimeMillis); /*实际上添加到looper类中的成员变量mQueue的队列中*/

}

else {

RuntimeException e = newRuntimeException(

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

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

}

return sent;

}

//就是这个函数,looper中的loop()函数里面是不是有一个该函数的调用

public voiddispatchMessage(Message msg) {

/*如果msg本身设置了callback,则直接交给这个callback处理了,所以我前面说它不一定真的调用handler的handleMessage()函数*/

if (msg.callback != null) {

handleCallback(msg);

} else {

//如果判断msg.callback为空,该handler的callback有定义,就用它了,注意这里的handleMessage()是handler的,在handler里面该函数什么都不做

if (mCallback != null) {

if(mCallback.handleMessage(msg)) {

return;

}

}

//这里是handler的派生类,所以handler的子类要重载该函数

handleMessage(msg);

}

}

..........

}

(3)Thread

为什么在开发过程中我们需要重新创建线程呢?大家知道我们创建的Service、Activity以及Broadcast均是一个主线程处理,这里我们可以理解为UI线程。但是在操作一些耗时操作时,比如 I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以考虑使用Thread线程来解决。

每个线程都与looper对象一一对应,对于从事过J2ME开发的程序员来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。

所在的文件Thread.java,线程负责调度整个消息循环,即消息循环的执行场所,代码如下:

public class Thread implementsRunnable {

private staticfinal int NANOS_PER_MILLI = 1000000;

/* some of theseare accessed directly by the VM; do not rename them */

volatile VMThread vmThread;

volatile ThreadGroup group;

volatile boolean daemon;

volatile String name;

volatile int priority;

volatile long stackSize;

Runnable target;

private static int count = 0;

……

……

/**

* Constructs a new {@code Thread} with a{@code Runnable} object and name

* provided. The new {@code Thread} willbelong to the same {@code

* ThreadGroup} as the {@code Thread}calling this constructor.

*

* @param runnable

* a {@code Runnable} whose method<code>run</code> will be

* executed by the new {@code Thread}

* @param threadName

* the name for the {@code Thread}being created

*

* @see java.lang.ThreadGroup

* @see java.lang.Runnable

*

* @since Android 1.0

*/

/*Thread的构造函数实在是多,这里不必一一列举,总之,他们都是通过调用Create()函数来实现*/

public Thread(Runnable runnable, StringthreadName) {

if (threadName == null) {

throw new NullPointerException();

}

create(null, runnable, threadName, 0);

}

/**

* Constructs a new {@code Thread} with no{@code Runnable} object and the

* name provided. The new {@code Thread}will belong to the same {@code

* ThreadGroup} as the {@code Thread}calling this constructor.

*

* @param threadName

* the name for the {@code Thread}being created

*

* @see java.lang.ThreadGroup

* @see java.lang.Runnable

*

* @since Android 1.0

*/

/*因为经常会用到这个构造函数,所以在这里列出来,我们后面以Wifi为例说明时就是用的这个函数*/

public Thread(String threadName) {

if (threadName == null) {

throw new NullPointerException();

}

create(null, null, threadName, 0);

}

/*这是参数最全的构造函数

publicThread(ThreadGroup group, Runnable runnable, String threadName, long stackSize){

if (threadName == null) {

throw new NullPointerException();

}

create(group, runnable, threadName,stackSize);

}

……

……

/**

* Initializes a new, existing Threadobject with a runnable object,

* the given name and belonging to theThreadGroup passed as parameter.

* This is the method that the severalpublic constructors delegate their

* work to.

*

* @param group ThreadGroup to which thenew Thread will belong

* @param runnable a java.lang.Runnablewhose method <code>run</code> will

*be executed by the new Thread

* @param threadName Name for the Threadbeing created

* @param stackSize Platform dependentstack size

* @throws SecurityException if<code>group.checkAccess()</code> fails

*with a SecurityException

* @throws IllegalThreadStateException if<code>group.destroy()</code> has

*already been done

* @see java.lang.ThreadGroup

* @see java.lang.Runnable

* @see java.lang.SecurityException

* @see java.lang.SecurityManager

*/

private void create(ThreadGroup group,Runnable runnable, String threadName, long stackSize) {

SecurityManager smgr =System.getSecurityManager();

if (smgr != null) {

if (group == null) {

group = smgr.getThreadGroup();

}

/*

* Freaky security requirement: Ifthe Thread's class is actually

* a subclass of Thread and ittries to override either

* getContextClassLoader() orsetContextClassLoader(), the

* SecurityManager has to allowthis.

*/

if (getClass() != Thread.class) {

Class[] signature = new Class[]{ ClassLoader.class };

try {

getClass().getDeclaredMethod("getContextClassLoader",signature);

smgr.checkPermission(newRuntimePermission("enableContextClassLoaderOverride"));

} catch (NoSuchMethodExceptionex) {

// Ignore. Just interestedin the method's existence.

}

try {

getClass().getDeclaredMethod("setContextClassLoader",signature);

smgr.checkPermission(newRuntimePermission("enableContextClassLoaderOverride"));

} catch (NoSuchMethodExceptionex) {

// Ignore. Just interested in the method'sexistence.

}

}

}

//获得当前线程

Thread currentThread =Thread.currentThread();

if (group == null) {

group =currentThread.getThreadGroup();

}

group.checkAccess();

if (group.isDestroyed()) {

throw newIllegalThreadStateException("Group already destroyed");

}

this.group = group;

synchronized (Thread.class) {

id = ++Thread.count;

}

if (threadName == null) {

this.name = "Thread-" +id;

} else {

this.name = threadName;

}

/*runnable是一个接口,声明了一个Run()函数,Thread实现了该函数,它实际上真正的线程体*/

this.target = runnable;

this.stackSize = stackSize;//线程的堆栈大小

this.priority =currentThread.getPriority();//线程的优先级

this.contextClassLoader =currentThread.contextClassLoader;

// Transfer overInheritableThreadLocals.

if (currentThread.inheritableValues !=null) {

inheritableValues

= newThreadLocal.Values(currentThread.inheritableValues);

}

// store current AccessControlContextas inherited context for this thread

SecurityUtils.putContext(this,AccessController.getContext());

// add ourselves to our ThreadGroup ofchoice

this.group.addThread(this); //将当前线程加入线程组

}

……

……

/**

* Starts the new Thread of execution. The<code>run()</code> method of

* the receiver will be called by thereceiver Thread itself (and not the

* Thread calling<code>start()</code>).

*

* @throws IllegalThreadStateException ifthe Thread has been started before

*

* @see Thread#run

*

* @since Android 1.0

*/

//这个函数用来启动一个新的线程,不过要注意的是,调用该函数后,线程并未真正运行,run()函数才是真正的线程体,这时它只是做好了运行准备,待系统在适当时机运行*/

public synchronizedvoid start() {

if (hasBeenStarted) {

throw newIllegalThreadStateException("Thread already started."); // TODOExternalize?

}

hasBeenStarted = true;

VMThread.create(this, stackSize);

}

……

……

对于Thread类,上面只列出了几个重要函数,在讨论本文话题中,其实只需要知道一个线程的创建和启动方法就行了,Thread对于本文话题的重要部分在其子类的run()线程体代码的编写上。

(4)简单的例子

有了上面的讲解你是不是有所理解了呢?好,我们先来创建一个非常简单的例子,只写出重要部分,不花时间考虑代码的完整和严谨性,以使例子更易于理解:

/*根据上面代码的讲解,我们很容易将looper和thread联系起来,并且建立looper和handler的联系,首先我们要从Thread派生一个出一个子类*/

class HandleThread extendsThread{

pubic HandleThread(){

super(“xxx”);

}

Looper myLooper = null;

//重载run函数

run(){

Looper.prepare();//将Looper设置到这个线程中

myLooper = Looper.myLooper();

myLooper.loop();开启消息循环

}

……

……

}

......

......

//创建一个线程

onCreate(...){

……

……

HandleThread threadtest= new HandleThread(); //定义一个测试线程

threadtest.start(); //启动测试线程

Looper looper = threadtest.myLooper; //再次用到myLooper获得线程拥有的looper

/*将looper作为参数创建handler对象,从前面内容,知道Handlertest对象可以直接操作looper的消息队列,因为它们共用一个mQueue*/

Handler Handlertest = new Handler(looper);

Handlertest.sendMessage(...);//向mQueue消息队列添加消息

……

……

}

通过上面的例子,相信你已经对这三个类和他们之间的关系有一定的了解了吧,对于MessageQueue类,说白了,就是用来表示一个队列的类,后面会有图示,就不用过多介绍了。

(5)handlerthread

我们上面的例子是超级简单的,在android中handlerThread类会稍微复杂一些,下面就来看看它的代码:

/**

* Copyright (C) 2006 TheAndroid Open Source Project

*

* Licensed under theApache License, Version 2.0 (the "License");

* you may not use thisfile except in compliance with the License.

* You may obtain a copyof the License at

*

*http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required byapplicable law or agreed to in writing, software

* distributed under theLicense is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES ORCONDITIONS OF ANY KIND, either express or implied.

* See the License forthe specific language governing permissions and

* limitations under theLicense.

*/

package android.os;

/***

* Handy class forstarting a new thread that has a looper. The looper can then be

* used to create handlerclasses. Note that start() must still be called.

*/

public class HandlerThread extends Thread {

private intmPriority; //线程优先级

private int mTid =-1; //线程ID

private LoopermLooper; //looper

//常用的handlerthread构造函数

publicHandlerThread(String name) {

super(name);

mPriority =Process.THREAD_PRIORITY_DEFAULT;

}

/***

* Constructs aHandlerThread.

* @param name

* @param priorityThe priority to run the thread at. The value supplied must be from

* {@linkandroid.os.Process} and not from java.lang.Thread.

*/

publicHandlerThread(String name, int priority) {

super(name);

mPriority =priority;

}

/***

* Call back methodthat can be explicitly over ridden if needed to execute some

* setup beforeLooper loops.

*/

protected void onLooperPrepared() {

}

//线程主体,创建了looper对象,并进入消息处理循环

public void run() {

mTid =Process.myTid();

Looper.prepare(); //初始化looper

synchronized(this) {

mLooper =Looper.myLooper();

notifyAll(); //唤醒原语

}

Process.setThreadPriority(mPriority);

onLooperPrepared();

Looper.loop(); //消息处理循环

mTid = -1;

}

/***

* This methodreturns the Looper associated with this thread. If this thread not been started

* or for any reasonis isAlive() returns false, this method will return null. If this thread

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

* @return Thelooper.

*/

public Looper getLooper() {

if (!isAlive()) {

return null;

}

// If the threadhas been started, wait until the looper has been created.

synchronized(this) {

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

try {

wait();

} catch(InterruptedException e) {

}

}

}

return mLooper;

}

/***

* Ask the currentlyrunning looper to quit. If the threadhas not

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

* null), then falseis returned. Otherwise the looper isasked to

* quit and true isreturned.

*/

public boolean quit(){

Looper looper =getLooper();

if (looper !=null) {

looper.quit();

return true;

}

return false;

}

/***

* Returns theidentifier of this thread. See Process.myTid().

*/

public intgetThreadId() {

return mTid;

}

}

介绍过了handlerthread,那么下面就可以来探讨挖掘出他们在android消息处理机制中,各自扮演的角色和相互的联系了。

二,android消息处理机制中各类之间关系

(一)MessageQueue,looper和handler的职责及相互之间的关系。

(1)职责描述:

Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。

MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。这个类用于为线程执行一个消息循环。Looper的实例与一个线程绑定,但是线程默认是没有Looper对象与其绑定的,可以通过在线程上调用Looper.prepare()绑定一个Looper对象到当前线程,之后调用Looper.loop()方法执行消息循环,直到循环被停止(遇到一个target=null的消息,就会退出当前消息循环)。

(2)关系描述:

Handler,Looper和MessageQueue就是简单的三角关系,如下图示。

Looper和MessageQueue一一对应,创建一个Looper的同时,会创建一个MessageQueue。而Handler与它们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper和MessageQueue。

所以,多个Handler都可以共享同一Looper和MessageQueue了。当然,这些Handler也就必须要运行在同一个线程里。


图2.1.1 Handler,Looper和MessageQueue关系图


下图是从Froyo从抽取出的HandlerThread、Looper和MessageQueue的关系图。他们被定义在package android.os。

图示2.1.2 HandlerThread/Looper/MessageQueue实例关系

(二)looper和handlerThread之间联系的动态建立过程(重点)

图示2.2.1 messageQueue,looper和handlerthread实例化过程

HandlerThread是一个Thread的子类,在它被实例化并执行start()之后,它的run()方法会在它自己的线程空间执行。上面我们已经提到了Looper的实例化是通过Looper::prepare实现的,图中可以看到Looper::prepare()正是在HandlerThread::run()中被调用而实例化的。而MessageQueue是在Looper的私有构造函数Looper()中实例化的,这些可以回头参考上面的代码。至此,messageQueue,looper和handlerthread实例化过程结束,后面调用loop()进入消息处理循环。当然,如果你想在工作线程中(这个子线程)处理消息的话,在进入消息处理循环之前,你也可以定义并初始化handler对象。

总结一下,HandlerThread是被显式地通过new创建的实例,而与它绑定在一起的Looper是在HandlerThread的执行过程中被实例化的,相应的MessageQueue也是在这个过程中实例化的。

上图说是它们实例化过程,其实并不完全是,刚才提到了loop()这个函数,它可是android消息机制里面非常重要的一个函数,前面第一部分代码中有说过它的工作内容,这里我们用工作流程图来分析:

图示2.2.2 Looper::loop()函数的工作流程图

这里是不是一目了然了,Looper::loop()是这个消息处理的核心,图中的所有序列是发生在一个无限循环中的。mQueue:MessageQueue是Looper保留的一份引用,通过它的next()[序列1]获取 MessageQueue中的下一个要处理的消息,这个过程中如果没有相应的消息,执行它的线程会用this.wait()释放它所拥有的MessageQueue的对象锁而等待。一旦有消息到来[序列2],Looper会用获得的Message的Handler(msg.target)来分发处理消息[序列3&4]。消息处理完之后,还要回收[序列5]。

(三)以Wifi模块为例,代码分析android的消息机制

在Looper::loop()消息处理的顺序图里看到了Handler,这个消息分发处理的参与者。下面结合WifiHandler这个具体的Handler看它如何参与到消息的发送、分发和处理的。

(1)handler的实例化,WifiService中定义了如下的WifiHandler。

图示2.3.1: handler和wifihandler类静态关系图

由图示可以看出Handler的构造函数是需要Looper的,从上面的分析知道,Looper是在HandlerThread执行中实例化的, HandlerThread保留着Looper的应用mLooper,并可通过getLooper()被外面 获取。而Handler的mQueue: MessageQueue可以通过mLooper.mQueue获得。所以,Wifi的HandlerThread,WifiHandler可以这样实例化:

{

......

HandlerThreadwifiThread=newHandlerThread("WifiService");

wifiThread.start();

mWifiHandler=newWifiHandler(wifiThread.getLooper());

......

}

(2)消息发送

图示2.3.2:消息发送

通过Message::obtain()可以建立起Message和Target/Handler,what之间的关系,并得到一个Message msg[序列1&2];然后通过msg.sendToTarget()就可以用Handler来具体发送消息了[序列3&4];通过一系 列的调用,最后会通过MessageQueue::enqueueMessage()把消息放到mMessages上[序列7],还会通过 this.notify()通知正在等待新消息的线程,重新拥有MessageQueue的对象锁,而处理该消息。

图示2.3.3:MessageQueue与Message的关系

(4)消息处理

具体处理消息,整个框架的最后消息的处理是通过Handler::handleMessager(msg: Message)来完成。所以如果有自己具体要处理的消息,只要override Handler的handleMessage(msg: Message)方法,并加入自己特定的对消息的处理即可。要处理消息,就看看Message的属性里都有什么。

图示2.3.4:message类图

重点关注public属性what是区分具体什么消息;可以带参数arg1, arg2。至此,消息的发送,分发和处理就介绍完毕了。

三,以线程的角度分析,使用该机制的技巧和注意的问题

在android中,Activity,Service属于主线程,在主线程中才能更新UI,如toast等。其他线程中不能直接使用更新UI,这时可以使用Handler来处理,Handler可以在Activity和Service中定义。

熟悉Windows编程的朋友可能知道Windows程序是消息驱动的,并且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制。实际上谷歌参考了Windows的消息循环机制,也在Android系统中实现了消息循环机制。Android通过 Looper、Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。本文深入介绍一下 Android消息处理系统原理。

Android系统中Looper负责管理线程的消息队列和消息循环,具体实现请参考上面的Looper的源码。 可以通过Loop.myLooper()得到当前线程的Looper对象(这是我们一再强调的重点),通过调用Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。

前面提到Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消息循环(Looper)。特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该线程具有消息队列和消息循环功能,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。

如下例 所示:

class LooperThread extends Thread {

public HandlermHandler;

public void run() {

Looper.prepare();

mHandler = newHandler(Looper.myLooper()) {

public voidhandleMessage(Message msg) {

// processincoming messages here

}

};

Looper.loop();

}

}

这样你的线程就具有了消息处理机制了,在Handler中进行消息处理。

Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件。 Handler的作用是把消息加入特定的(Looper)消息队列中,并分发和处理该消息队列中的消息。构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper创建。

一个Activity中可以创建多个工作线程或者其他的组件,如果这些线程或者组件把他们的消息放入Activity的主线程消息队列,那么该消息就会在主线程中处理了。因为主线程一般负责界面的更新操作,并且Android系统中的weget不是线程安全的,所以这种方式可以很好的实现 Android界面更新。在Android系统中这种方式有着广泛的运用。

那么另外一个线程怎样把消息放入主线程的消息队列呢?答案是通过Handle对象,只要Handler对象以主线程中创建,并用主线程的的Looper创建,那么调用 Handler的sendMessage等接口,将会把消息放入队列都将是放入主线程的消息队列(这是handler与looper共用一个MessageQueue的结果)。并且将会在Handler主线程中调用该handler 的handleMessage接口来处理消息。

这里面涉及到线程同步问题,请先参考如下例子来理解Handler对象的线程模型:

(1)首先创建MyHandler工程。

(2)在MyHandler.java中加入如下的代码:

package com.simon;

import android.app.Activity;

import android.os.Bundle;

import android.os.Message;

import android.util.Log;

import android.os.Handler;

public class MyHandler extends Activity {

static final StringTAG = "Handler";

Handler h = newHandler(){ //创建处理对象,并定义handleMessage()函数

public voidhandleMessage (Message msg)
{
switch(msg.what)
{
case HANDLER_TEST:
Log.d(TAG, "Thehandler thread id = " + Thread.currentThread().getId() + "\n");
break;
}
}
};

static final int HANDLER_TEST = 1;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "The main thread id =" + Thread.currentThread().getId() + "\n");

newmyThread().start(); //创建子线程对象,并启动子线程

setContentView(R.layout.main);
}

class myThread extends Thread

{

//这个线程计较简单,Run()函数只是调用了在主线程中生成的处理类对象的一个发送消息的函数,没有单独生成looper进入循环

public void run()
{
Message msg = new Message();

msg.what =HANDLER_TEST;
h.sendMessage(msg);
Log.d(TAG, "The worker threadid = " + Thread.currentThread().getId() + "\n");
}
}
}

在这个例子中我们主要是打印,这种处理机制各个模块的所处的线程情况。如下是机器运行结果:

DEBUG/Handler(302):The main thread id = 1
DEBUG/Handler(302): The worker threadid = 8
DEBUG/Handler(302): The handler threadid = 1

我们可以看出消息处理是在主线程中处理的,在消息处理函数中可以安全的调用主线程中的任何资源,包括刷新界面。工作线程和主线程运行在不同的线程中,所以必须要注意这两个线程间的竞争关系。

上例中,你可能注意到在工作线程中访问了主线程handler对象,并在调用handler的对象向消息队列加入了一个消息。这个过程中会不会出现消息队列数据不一致问题呢?答案是handler对象不会出问题,因为handler对象管理的Looper对象是线程安全的,不管是加入消息到消息队列 和从队列读出消息都是有同步对象保护的。上例中没有修改handler对象,所以handler对象不可能会出现数据不一致的问题。

通过上面的分析,我们可以得出如下结论:

1、如果通过工作线程刷新界面,推荐使用handler对象来实现。

2、注意工作线程和主线程之间的竞争关系。推荐handler对象在主线程中构造完成(并且启动工作线程之后不要再修改之,否则会出现数据不一致),然后在工作线程中可以放心的调用发送消息SendMessage等接口。

3、除了2所述的hanlder对象之外的任何主线程的成员变量如果在工作线程中调用,仔细考虑线程同步问题。如果有必要需要加入同步对象保护该变量。

4、handler对象的handleMessage接口将会在主线程中调用。在这个函数可以放心的调用主线程中任何变量和函数,进而完成更新UI的任务。

5、Android很多API也利用Handler这种线程特性,作为一种回调函数的变种,来通知调用者。这样Android框架就可以在其线程中将消息发送到调用者的线程消息队列之中,不用担心线程同步的问题。

6. 下面再从线程的角度看一下,消息处理过程中参与的线程,以及这些线程之间的同步。显然的,这里有线程HandlerThread的参 与,而且Looper::loop()就是执行在HandlerThread的run()方法里,也就是在HandlerThread里执行,这也就是说 消息的分发处理和执行是在HandlerThread的线程上下文中。另外,还有至少一个线程存在,也就是创建了HandlerThread的线程B,以 及执行消息发送的线程C,B和C有可能是同一线程。

消息的发送是在另外一个线程里,就是因为有了多个线程的存在,才有了线程的同步操作。可再次关注一下实现线程同步的Java原语wait()/notify()。

参考资料:

http://www.android123.com.cn/androidkaifa/422.html

http://www.android123.com.cn/androidkaifa/422.html

http://www.th7.cn/Article/bc/Android/201102/20110227210039.html


更多相关文章

  1. Android仿人人客户端(v5.7.1)——网络模块处理的架构
  2. 转: Android异步加载图像小结
  3. android Message的简单实例
  4. Android(安卓)中的WiFi学习笔记(转载)----WIFI启动 代码流程走读
  5. 【绝对干货】超全Android中高级面试复习大纲,覆盖所有面试知识!(上
  6. Android(安卓)异步加载解决方案
  7. Android(安卓)Framework解析
  8. Android在子线程当中更新UI界面(TextView、ImageView)
  9. 箭头函数的基础使用

随机推荐

  1. Android(安卓)POI操作office文档
  2. Android(安卓)ListView实现快速定位联系
  3. 基于 Feign 实现强类型接口
  4. Android调用摄像头拍照(兼容7.0)
  5. 用android build system 编译一个最小的a
  6. Android学习整理-2-TabLayout的学习
  7. android 设置Spinner文字标题颜色 字体大
  8. 解决移动端Web安卓机点击输入框,输入法遮
  9. Android(安卓)Studio插件
  10. Android——Activity之间传递 实体类(Bea