Handler和Looper分析

Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,应用程序的主线程不断地从这个消息队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序的执行,本文将详细分析Android应用程序的消息处理机制。

前面我们学习Android应用程序中的Activity启动(Android应用程序启动过程源代码分析和Android应用程序内部启动Activity过程(startActivity)的源代码分析)、Service启动(Android系统在新进程中启动自定义服务过程(startService)的原理分析和Android应用程序绑定服务(bindService)的过程源代码分析)以及广播发送(Android应用程序发送广播(sendBroadcast)的过程分析)时,它们都有一个共同的特点,当ActivityManagerService需要与应用程序进行并互时,如加载Activity和Service、处理广播待,会通过Binder进程间通信机制来知会应用程序,应用程序接收到这个请求时,它不是马上就处理这个请求,而是将这个请求封装成一个消息,然后把这个消息放在应用程序的消息队列中去,然后再通过消息循环来处理这个消息。这样做的好处就是消息的发送方只要把消息发送到应用程序的消息队列中去就行了,它可以马上返回去处理别的事情,而不需要等待消息的接收方去处理完这个消息才返回,这样就可以提高系统的并发性。实质上,这就是一种异步处理机制。

这样说可能还是比较笼统,我们以Android应用程序启动过程源代码分析一文中所介绍的应用程序启动过程的一个片断来具体看看是如何这种消息处理机制的。在这篇文章中,要启动的应用程序称为Activity,它的默认Activity是MainActivity,它是由Launcher来负责启动的,而Launcher又是通过ActivityManagerService来启动的,当ActivityManagerService为这个即将要启的应用程序准备好新的进程后,便通过一个Binder进程间通信过程来通知这个新的进程来加载MainActivity,如下图所示:


它对应Android应用程序启动过程中的Step 30到Step 35,有兴趣的读者可以回过头去参考Android应用程序启动过程源代码分析一文。这里的Step 30中的scheduleLaunchActivity是ActivityManagerService通过Binder进程间通信机制发送过来的请求,它请求应用程序中的ActivityThread执行Step 34中的performLaunchActivity操作,即启动MainActivity的操作。这里我们就可以看到,Step 30的这个请求并没有等待Step 34这个操作完成就返回了,它只是把这个请求封装成一个消息,然后通过Step 31中的queueOrSendMessage操作把这个消息放到应用程序的消息队列中,然后就返回了。应用程序发现消息队列中有消息时,就会通过Step 32中的handleMessage操作来处理这个消息,即调用Step 33中的handleLaunchActivity来执行实际的加载MainAcitivy类的操作。

了解Android应用程序的消息处理过程之后,我们就开始分样它的实现原理了。与Windows应用程序的消息处理过程一样,Android应用程序的消息处理机制也是由消息循环、消息发送和消息处理这三个部分组成的,接下来,我们就详细描述这三个过程。

1. 消息循环

在消息处理机制中,消息都是存放在一个消息队列中去,而应用程序的主线程就是围绕这个消息队列进入一个无限循环的,直到应用程序退出。如果队列中有消息,应用程序的主线程就会把它取出来,并分发给相应的Handler进行处理;如果队列中没有消息,应用程序的主线程就会进入空闲等待状态,等待下一个消息的到来。在Android应用程序中,这个消息循环过程是由Looper类来实现的,它定义在frameworks/base/core/java/android/os/Looper.java文件中,在分析这个类之前,我们先看一下Android应用程序主线程是如何进入到这个消息循环中去的。

在Android应用程序进程启动过程的源代码分析一文中,我们分析了Android应用程序进程的启动过程,Android应用程序进程在启动的时候,会在进程中加载ActivityThread类,并且执行这个类的main函数,应用程序的消息循环过程就是在这个main函数里面实现的,我们来看看这个函数的实现,它定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plain copy
  1. publicfinalclassActivityThread{
  2. ......
  3. publicstaticfinalvoidmain(String[]args){
  4. ......
  5. Looper.prepareMainLooper();
  6. ......
  7. ActivityThreadthread=newActivityThread();
  8. thread.attach(false);
  9. ......
  10. Looper.loop();
  11. ......
  12. thread.detach();
  13. ......
  14. }
  15. }
这个函数做了两件事情,一是在主线程中创建了一个ActivityThread实例,二是通过Looper类使主线程进入消息循环中,这里我们只关注后者。

首先看Looper.prepareMainLooper函数的实现,这是一个静态成员函数,定义在frameworks/base/core/java/android/os/Looper.java文件中:

[java] view plain copy
  1. publicclassLooper{
  2. ......
  3. privatestaticfinalThreadLocalsThreadLocal=newThreadLocal();
  4. finalMessageQueuemQueue;
  5. ......
  6. /**Initializethecurrentthreadasalooper.
  7. *Thisgivesyouachancetocreatehandlersthatthenreference
  8. *thislooper,beforeactuallystartingtheloop.Besuretocall
  9. *{@link#loop()}aftercallingthismethod,andenditbycalling
  10. *{@link#quit()}.
  11. */
  12. publicstaticfinalvoidprepare(){
  13. if(sThreadLocal.get()!=null){
  14. thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");
  15. }
  16. sThreadLocal.set(newLooper());
  17. }
  18. /**Initializethecurrentthreadasalooper,markingitasanapplication'smain
  19. *looper.ThemainlooperforyourapplicationiscreatedbytheAndroidenvironment,
  20. *soyoushouldneverneedtocallthisfunctionyourself.
  21. *{@link#prepare()}
  22. */
  23. publicstaticfinalvoidprepareMainLooper(){
  24. prepare();
  25. setMainLooper(myLooper());
  26. if(Process.supportsProcesses()){
  27. myLooper().mQueue.mQuitAllowed=false;
  28. }
  29. }
  30. privatesynchronizedstaticvoidsetMainLooper(Looperlooper){
  31. mMainLooper=looper;
  32. }
  33. /**
  34. *ReturntheLooperobjectassociatedwiththecurrentthread.Returns
  35. *nullifthecallingthreadisnotassociatedwithaLooper.
  36. */
  37. publicstaticfinalLoopermyLooper(){
  38. return(Looper)sThreadLocal.get();
  39. }
  40. privateLooper(){
  41. mQueue=newMessageQueue();
  42. mRun=true;
  43. mThread=Thread.currentThread();
  44. }
  45. ......
  46. }
函数prepareMainLooper做的事情其实就是在线程中创建一个Looper对象,这个Looper对象是存放在sThreadLocal成员变量里面的,成员变量sThreadLocal的类型为ThreadLocal,表示这是一个线程局部变量,即保证每一个调用了prepareMainLooper函数的线程里面都有一个独立的Looper对象。在线程是创建Looper对象的工作是由prepare函数来完成的,而在创建Looper对象的时候,会同时创建一个消息队列MessageQueue,保存在Looper的成员变量mQueue中,后续消息就是存放在这个队列中去。消息队列在Android应用程序消息处理机制中最重要的组件,因此,我们看看它的创建过程,即它的构造函数的实现,实现frameworks/base/core/java/android/os/MessageQueue.java文件中:

[java] view plain copy
  1. publicclassMessageQueue{
  2. ......
  3. privateintmPtr;//usedbynativecode
  4. privatenativevoidnativeInit();
  5. MessageQueue(){
  6. nativeInit();
  7. }
  8. ......
  9. }
它的初始化工作都交给JNI方法nativeInit来实现了,这个JNI方法定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:

[cpp] view plain copy
  1. staticvoidandroid_os_MessageQueue_nativeInit(JNIEnv*env,jobjectobj){
  2. NativeMessageQueue*nativeMessageQueue=newNativeMessageQueue();
  3. if(!nativeMessageQueue){
  4. jniThrowRuntimeException(env,"Unabletoallocatenativequeue");
  5. return;
  6. }
  7. android_os_MessageQueue_setNativeMessageQueue(env,obj,nativeMessageQueue);
  8. }
在JNI中,也相应地创建了一个消息队列NativeMessageQueue,NativeMessageQueue类也是定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中,它的创建过程如下所示:

[cpp] view plain copy
  1. NativeMessageQueue::NativeMessageQueue(){
  2. mLooper=Looper::getForThread();
  3. if(mLooper==NULL){
  4. mLooper=newLooper(false);
  5. Looper::setForThread(mLooper);
  6. }
  7. }
它主要就是在内部创建了一个Looper对象,注意,这个Looper对象是实现在JNI层的,它与上面Java层中的Looper是不一样的,不过它们是对应的,下面我们进一步分析消息循环的过程的时候,读者就会清楚地了解到它们之间的关系。

这个Looper的创建过程也很重要,不过我们暂时放一放,先分析完android_os_MessageQueue_nativeInit函数的执行,它创建了本地消息队列NativeMessageQueue对象之后,接着调用android_os_MessageQueue_setNativeMessageQueue函数来把这个消息队列对象保存在前面我们在Java层中创建的MessageQueue对象的mPtr成员变量里面:

[cpp] view plain copy
  1. staticvoidandroid_os_MessageQueue_setNativeMessageQueue(JNIEnv*env,jobjectmessageQueueObj,
  2. NativeMessageQueue*nativeMessageQueue){
  3. env->SetIntField(messageQueueObj,gMessageQueueClassInfo.mPtr,
  4. reinterpret_cast<jint>(nativeMessageQueue));
  5. }
这里传进来的参数messageQueueObj即为我们前面在Java层创建的消息队列对象,而gMessageQueueClassInfo.mPtr即表示在Java类MessageQueue中,其成员变量mPtr的偏移量,通过这个偏移量,就可以把这个本地消息队列对象natvieMessageQueue保存在Java层创建的消息队列对象的mPtr成员变量中,这是为了后续我们调用Java层的消息队列对象的其它成员函数进入到JNI层时,能够方便地找回它在JNI层所对应的消息队列对象。

我们再回到NativeMessageQueue的构造函数中,看看JNI层的Looper对象的创建过程,即看看它的构造函数是如何实现的,这个Looper类实现在frameworks/base/libs/utils/Looper.cpp文件中:

[cpp] view plain copy
  1. Looper::Looper(boolallowNonCallbacks):
  2. mAllowNonCallbacks(allowNonCallbacks),
  3. mResponseIndex(0){
  4. intwakeFds[2];
  5. intresult=pipe(wakeFds);
  6. ......
  7. mWakeReadPipeFd=wakeFds[0];
  8. mWakeWritePipeFd=wakeFds[1];
  9. ......
  10. #ifdefLOOPER_USES_EPOLL
  11. //Allocatetheepollinstanceandregisterthewakepipe.
  12. mEpollFd=epoll_create(EPOLL_SIZE_HINT);
  13. ......
  14. structepoll_eventeventItem;
  15. memset(&eventItem,0,sizeof(epoll_event));//zerooutunusedmembersofdatafieldunion
  16. eventItem.events=EPOLLIN;
  17. eventItem.data.fd=mWakeReadPipeFd;
  18. result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);
  19. ......
  20. #else
  21. ......
  22. #endif
  23. ......
  24. }
这个构造函数做的事情非常重要,它跟我们后面要介绍的应用程序主线程在消息队列中没有消息时要进入等待状态以及当消息队列有消息时要把应用程序主线程唤醒的这两个知识点息息相关。它主要就是通过pipe系统调用来创建了一个管道了:

[cpp] view plain copy
  1. intwakeFds[2];
  2. intresult=pipe(wakeFds);
  3. ......
  4. mWakeReadPipeFd=wakeFds[0];
  5. mWakeWritePipeFd=wakeFds[1];
管道是Linux系统中的一种进程间通信机制,具体可以参考前面一篇文章 Android学习启动篇 推荐的一本书《Linux内核源代码情景分析》中的第6章--传统的Uinx进程间通信。简单来说,管道就是一个文件,在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的,一般的使用方式就是,一个线程通过读文件描述符中来读管道的内容,当管道没有内容时,这个线程就会进入等待状态,而另外一个线程通过写文件描述符来向管道中写入内容,写入内容的时候,如果另一端正有线程正在等待管道中的内容,那么这个线程就会被唤醒。这个等待和唤醒的操作是如何进行的呢,这就要借助Linux系统中的epoll机制了。Linux系统中的epoll机制为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。但是这里我们其实只需要监控的IO接口只有mWakeReadPipeFd一个,即前面我们所创建的管道的读端,为什么还需要用到epoll呢?有点用牛刀来杀鸡的味道。其实不然,这个Looper类是非常强大的,它除了监控内部所创建的管道接口之外,还提供了addFd接口供外界面调用,外界可以通过这个接口把自己想要监控的IO事件一并加入到这个Looper对象中去,当所有这些被监控的IO接口上面有事件发生时,就会唤醒相应的线程来处理,不过这里我们只关心刚才所创建的管道的IO事件的发生。

要使用Linux系统的epoll机制,首先要通过epoll_create来创建一个epoll专用的文件描述符:

[cpp] view plain copy
  1. mEpollFd=epoll_create(EPOLL_SIZE_HINT);
传入的参数EPOLL_SIZE_HINT是在这个mEpollFd上能监控的最大文件描述符数。

接着还要通过epoll_ctl函数来告诉epoll要监控相应的文件描述符的什么事件:

[cpp] view plain copy
  1. structepoll_eventeventItem;
  2. memset(&eventItem,0,sizeof(epoll_event));//zerooutunusedmembersofdatafieldunion
  3. eventItem.events=EPOLLIN;
  4. eventItem.data.fd=mWakeReadPipeFd;
  5. result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);
这里就是告诉mEpollFd,它要监控mWakeReadPipeFd文件描述符的EPOLLIN事件,即当管道中有内容可读时,就唤醒当前正在等待管道中的内容的线程。
C++层的这个Looper对象创建好了之后,就返回到JNI层的NativeMessageQueue的构造函数,最后就返回到Java层的消息队列MessageQueue的创建过程,这样,Java层的Looper对象就准备好了。有点复杂,我们先小结一下这一步都做了些什么事情:

A. 在Java层,创建了一个Looper对象,这个Looper对象是用来进入消息循环的,它的内部有一个消息队列MessageQueue对象mQueue;

B. 在JNI层,创建了一个NativeMessageQueue对象,这个NativeMessageQueue对象保存在Java层的消息队列对象mQueue的成员变量mPtr中;

C. 在C++层,创建了一个Looper对象,保存在JNI层的NativeMessageQueue对象的成员变量mLooper中,这个对象的作用是,当Java层的消息队列中没有消息时,就使Android应用程序主线程进入等待状态,而当Java层的消息队列中来了新的消息后,就唤醒Android应用程序的主线程来处理这个消息。

回到ActivityThread类的main函数中,在上面这些工作都准备好之后,就调用Looper类的loop函数进入到消息循环中去了:

[cpp] view plain copy
  1. publicclassLooper{
  2. ......
  3. publicstaticfinalvoidloop(){
  4. Looperme=myLooper();
  5. MessageQueuequeue=me.mQueue;
  6. ......
  7. while(true){
  8. Messagemsg=queue.next();//mightblock
  9. ......
  10. if(msg!=null){
  11. if(msg.target==null){
  12. //Notargetisamagicidentifierforthequitmessage.
  13. return;
  14. }
  15. ......
  16. msg.target.dispatchMessage(msg);
  17. ......
  18. msg.recycle();
  19. }
  20. }
  21. }
  22. ......
  23. }
这里就是进入到消息循环中去了,它不断地从消息队列mQueue中去获取下一个要处理的消息msg,如果消息的target成员变量为null,就表示要退出消息循环了,否则的话就要调用这个target对象的dispatchMessage成员函数来处理这个消息,这个target对象的类型为Handler,下面我们分析消息的发送时会看到这个消息对象msg是如设置的。

这个函数最关键的地方便是从消息队列中获取下一个要处理的消息了,即MessageQueue.next函数,它实现frameworks/base/core/java/android/os/MessageQueue.java文件中:

[java] view plain copy
  1. publicclassMessageQueue{
  2. ......
  3. finalMessagenext(){
  4. intpendingIdleHandlerCount=-1;//-1onlyduringfirstiteration
  5. intnextPollTimeoutMillis=0;
  6. for(;;){
  7. if(nextPollTimeoutMillis!=0){
  8. Binder.flushPendingCommands();
  9. }
  10. nativePollOnce(mPtr,nextPollTimeoutMillis);
  11. synchronized(this){
  12. //Trytoretrievethenextmessage.Returniffound.
  13. finallongnow=SystemClock.uptimeMillis();
  14. finalMessagemsg=mMessages;
  15. if(msg!=null){
  16. finallongwhen=msg.when;
  17. if(now>=when){
  18. mBlocked=false;
  19. mMessages=msg.next;
  20. msg.next=null;
  21. if(Config.LOGV)Log.v("MessageQueue","Returningmessage:"+msg);
  22. returnmsg;
  23. }else{
  24. nextPollTimeoutMillis=(int)Math.min(when-now,Integer.MAX_VALUE);
  25. }
  26. }else{
  27. nextPollTimeoutMillis=-1;
  28. }
  29. //Iffirsttime,thengetthenumberofidlerstorun.
  30. if(pendingIdleHandlerCount<0){
  31. pendingIdleHandlerCount=mIdleHandlers.size();
  32. }
  33. if(pendingIdleHandlerCount==0){
  34. //Noidlehandlerstorun.Loopandwaitsomemore.
  35. mBlocked=true;
  36. continue;
  37. }
  38. if(mPendingIdleHandlers==null){
  39. mPendingIdleHandlers=newIdleHandler[Math.max(pendingIdleHandlerCount,4)];
  40. }
  41. mPendingIdleHandlers=mIdleHandlers.toArray(mPendingIdleHandlers);
  42. }
  43. //Runtheidlehandlers.
  44. //Weonlyeverreachthiscodeblockduringthefirstiteration.
  45. for(inti=0;i<pendingIdleHandlerCount;i++){
  46. finalIdleHandleridler=mPendingIdleHandlers[i];
  47. mPendingIdleHandlers[i]=null;//releasethereferencetothehandler
  48. booleankeep=false;
  49. try{
  50. keep=idler.queueIdle();
  51. }catch(Throwablet){
  52. Log.wtf("MessageQueue","IdleHandlerthrewexception",t);
  53. }
  54. if(!keep){
  55. synchronized(this){
  56. mIdleHandlers.remove(idler);
  57. }
  58. }
  59. }
  60. //Resettheidlehandlercountto0sowedonotrunthemagain.
  61. pendingIdleHandlerCount=0;
  62. //Whilecallinganidlehandler,anewmessagecouldhavebeendelivered
  63. //sogobackandlookagainforapendingmessagewithoutwaiting.
  64. nextPollTimeoutMillis=0;
  65. }
  66. }
  67. ......
  68. }
调用这个函数的时候,有可能会让线程进入等待状态。什么情况下,线程会进入等待状态呢?两种情况,一是当消息队列中没有消息时,它会使线程进入等待状态;二是消息队列中有消息,但是消息指定了执行的时间,而现在还没有到这个时间,线程也会进入等待状态。消息队列中的消息是按时间先后来排序的,后面我们在分析消息的发送时会看到。

执行下面语句是看看当前消息队列中有没有消息:

[java] view plain copy
  1. nativePollOnce(mPtr,nextPollTimeoutMillis);
这是一个JNI方法,我们等一下再分析,这里传入的参数mPtr就是指向前面我们在JNI层创建的NativeMessageQueue对象了,而参数nextPollTimeoutMillis则表示如果当前消息队列中没有消息,它要等待的时候,for循环开始时,传入的值为0,表示不等待。

当前nativePollOnce返回后,就去看看消息队列中有没有消息:

[java] view plain copy
  1. finalMessagemsg=mMessages;
  2. if(msg!=null){
  3. finallongwhen=msg.when;
  4. if(now>=when){
  5. mBlocked=false;
  6. mMessages=msg.next;
  7. msg.next=null;
  8. if(Config.LOGV)Log.v("MessageQueue","Returningmessage:"+msg);
  9. returnmsg;
  10. }else{
  11. nextPollTimeoutMillis=(int)Math.min(when-now,Integer.MAX_VALUE);
  12. }
  13. }else{
  14. nextPollTimeoutMillis=-1;
  15. }
如果消息队列中有消息,并且当前时候大于等于消息中的执行时间,那么就直接返回这个消息给Looper.loop消息处理,否则的话就要等待到消息的执行时间:

[java] view plain copy
  1. nextPollTimeoutMillis=(int)Math.min(when-now,Integer.MAX_VALUE);
如果消息队列中没有消息,那就要进入无穷等待状态直到有新消息了:

[java] view plain copy
  1. nextPollTimeoutMillis=-1;
-1表示下次调用nativePollOnce时,如果消息中没有消息,就进入无限等待状态中去。

这里计算出来的等待时间都是在下次调用nativePollOnce时使用的。

这里说的等待,是空闲等待,而不是忙等待,因此,在进入空闲等待状态前,如果应用程序注册了IdleHandler接口来处理一些事情,那么就会先执行这里IdleHandler,然后再进入等待状态。IdlerHandler是定义在MessageQueue的一个内部类:

[java] view plain copy
  1. publicclassMessageQueue{
  2. ......
  3. /**
  4. *Callbackinterfacefordiscoveringwhenathreadisgoingtoblock
  5. *waitingformoremessages.
  6. */
  7. publicstaticinterfaceIdleHandler{
  8. /**
  9. *Calledwhenthemessagequeuehasrunoutofmessagesandwillnow
  10. *waitformore.Returntruetokeepyouridlehandleractive,false
  11. *tohaveitremoved.Thismaybecallediftherearestillmessages
  12. *pendinginthequeue,buttheyareallscheduledtobedispatched
  13. *afterthecurrenttime.
  14. */
  15. booleanqueueIdle();
  16. }
  17. ......
  18. }
它只有一个成员函数queueIdle,执行这个函数时,如果返回值为false,那么就会从应用程序中移除这个IdleHandler,否则的话就会在应用程序中继续维护着这个IdleHandler,下次空闲时仍会再执会这个IdleHandler。MessageQueue提供了addIdleHandler和removeIdleHandler两注册和删除IdleHandler。

回到MessageQueue函数中,它接下来就是在进入等待状态前,看看有没有IdleHandler是需要执行的:

[java] view plain copy
  1. //Iffirsttime,thengetthenumberofidlerstorun.
  2. if(pendingIdleHandlerCount<0){
  3. pendingIdleHandlerCount=mIdleHandlers.size();
  4. }
  5. if(pendingIdleHandlerCount==0){
  6. //Noidlehandlerstorun.Loopandwaitsomemore.
  7. mBlocked=true;
  8. continue;
  9. }
  10. if(mPendingIdleHandlers==null){
  11. mPendingIdleHandlers=newIdleHandler[Math.max(pendingIdleHandlerCount,4)];
  12. }
  13. mPendingIdleHandlers=mIdleHandlers.toArray(mPendingIdleHandlers);
如果没有,即pendingIdleHandlerCount等于0,那下面的逻辑就不执行了,通过continue语句直接进入下一次循环,否则就要把注册在mIdleHandlers中的IdleHandler取出来,放在mPendingIdleHandlers数组中去。

接下来就是执行这些注册了的IdleHanlder了:

[java] view plain copy
  1. //Runtheidlehandlers.
  2. //Weonlyeverreachthiscodeblockduringthefirstiteration.
  3. for(inti=0;i<pendingIdleHandlerCount;i++){
  4. finalIdleHandleridler=mPendingIdleHandlers[i];
  5. mPendingIdleHandlers[i]=null;//releasethereferencetothehandler
  6. booleankeep=false;
  7. try{
  8. keep=idler.queueIdle();
  9. }catch(Throwablet){
  10. Log.wtf("MessageQueue","IdleHandlerthrewexception",t);
  11. }
  12. if(!keep){
  13. synchronized(this){
  14. mIdleHandlers.remove(idler);
  15. }
  16. }
  17. }
执行完这些IdleHandler之后,线程下次调用nativePollOnce函数时,就不设置超时时间了,因为,很有可能在执行IdleHandler的时候,已经有新的消息加入到消息队列中去了,因此,要重置nextPollTimeoutMillis的值:

[java] view plain copy
  1. //Whilecallinganidlehandler,anewmessagecouldhavebeendelivered
  2. //sogobackandlookagainforapendingmessagewithoutwaiting.
  3. nextPollTimeoutMillis=0;
分析完MessageQueue的这个next函数之后,我们就要深入分析一下JNI方法nativePollOnce了,看看它是如何进入等待状态的,这个函数定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:

[cpp] view plain copy
  1. staticvoidandroid_os_MessageQueue_nativePollOnce(JNIEnv*env,jobjectobj,
  2. jintptr,jinttimeoutMillis){
  3. NativeMessageQueue*nativeMessageQueue=reinterpret_cast<NativeMessageQueue*>(ptr);
  4. nativeMessageQueue->pollOnce(timeoutMillis);
  5. }
这个函数首先是通过传进入的参数ptr取回前面在Java层创建MessageQueue对象时在JNI层创建的NatvieMessageQueue对象,然后调用它的pollOnce函数:

[cpp] view plain copy
  1. voidNativeMessageQueue::pollOnce(inttimeoutMillis){
  2. mLooper->pollOnce(timeoutMillis);
  3. }
这里将操作转发给mLooper对象的pollOnce函数处理,这里的mLooper对象是在C++层的对象,它也是在前面在JNI层创建的NatvieMessageQueue对象时创建的,它的pollOnce函数定义在frameworks/base/libs/utils/Looper.cpp文件中:

[cpp] view plain copy
  1. intLooper::pollOnce(inttimeoutMillis,int*outFd,int*outEvents,void**outData){
  2. intresult=0;
  3. for(;;){
  4. ......
  5. if(result!=0){
  6. ......
  7. returnresult;
  8. }
  9. result=pollInner(timeoutMillis);
  10. }
  11. }
为了方便讨论,我们把这个函数的无关部分都去掉,它主要就是调用pollInner函数来进一步操作,如果pollInner返回值不等于0,这个函数就可以返回了。

函数pollInner的定义如下:

[cpp] view plain copy
  1. intLooper::pollInner(inttimeoutMillis){
  2. ......
  3. intresult=ALOOPER_POLL_WAKE;
  4. ......
  5. #ifdefLOOPER_USES_EPOLL
  6. structepoll_eventeventItems[EPOLL_MAX_EVENTS];
  7. inteventCount=epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis);
  8. boolacquiredLock=false;
  9. #else
  10. ......
  11. #endif
  12. if(eventCount<0){
  13. if(errno==EINTR){
  14. gotoDone;
  15. }
  16. LOGW("Pollfailedwithanunexpectederror,errno=%d",errno);
  17. result=ALOOPER_POLL_ERROR;
  18. gotoDone;
  19. }
  20. if(eventCount==0){
  21. ......
  22. result=ALOOPER_POLL_TIMEOUT;
  23. gotoDone;
  24. }
  25. ......
  26. #ifdefLOOPER_USES_EPOLL
  27. for(inti=0;i<eventCount;i++){
  28. intfd=eventItems[i].data.fd;
  29. uint32_tepollEvents=eventItems[i].events;
  30. if(fd==mWakeReadPipeFd){
  31. if(epollEvents&EPOLLIN){
  32. awoken();
  33. }else{
  34. LOGW("Ignoringunexpectedepollevents0x%xonwakereadpipe.",epollEvents);
  35. }
  36. }else{
  37. ......
  38. }
  39. }
  40. if(acquiredLock){
  41. mLock.unlock();
  42. }
  43. Done:;
  44. #else
  45. ......
  46. #endif
  47. ......
  48. returnresult;
  49. }
这里,首先是调用epoll_wait函数来看看epoll专用文件描述符mEpollFd所监控的文件描述符是否有IO事件发生,它设置监控的超时时间为timeoutMillis:

[cpp] view plain copy
  1. inteventCount=epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis);
回忆一下前面的Looper的构造函数,我们在里面设置了要监控mWakeReadPipeFd文件描述符的EPOLLIN事件。

当mEpollFd所监控的文件描述符发生了要监控的IO事件后或者监控时间超时后,线程就从epoll_wait返回了,否则线程就会在epoll_wait函数中进入睡眠状态了。返回后如果eventCount等于0,就说明是超时了:

[cpp] view plain copy
  1. if(eventCount==0){
  2. ......
  3. result=ALOOPER_POLL_TIMEOUT;
  4. gotoDone;
  5. }
如果eventCount不等于0,就说明发生要监控的事件:

[cpp] view plain copy
  1. for(inti=0;i<eventCount;i++){
  2. intfd=eventItems[i].data.fd;
  3. uint32_tepollEvents=eventItems[i].events;
  4. if(fd==mWakeReadPipeFd){
  5. if(epollEvents&EPOLLIN){
  6. awoken();
  7. }else{
  8. LOGW("Ignoringunexpectedepollevents0x%xonwakereadpipe.",epollEvents);
  9. }
  10. }else{
  11. ......
  12. }
  13. }
这里我们只关注mWakeReadPipeFd文件描述符上的事件,如果在mWakeReadPipeFd文件描述符上发生了EPOLLIN就说明应用程序中的消息队列里面有新的消息需要处理了,接下来它就会先调用awoken函数清空管道中把内容,以便下次再调用pollInner函数时,知道自从上次处理完消息队列中的消息后,有没有新的消息加进来。

函数awoken的实现很简单,它只是把管道中的内容都读取出来:

[cpp] view plain copy
  1. voidLooper::awoken(){
  2. ......
  3. charbuffer[16];
  4. ssize_tnRead;
  5. do{
  6. nRead=read(mWakeReadPipeFd,buffer,sizeof(buffer));
  7. }while((nRead==-1&&errno==EINTR)||nRead==sizeof(buffer));
  8. }
因为当其它的线程向应用程序的消息队列加入新的消息时,会向这个管道写入新的内容来通知应用程序主线程有新的消息需要处理了,下面我们分析消息的发送的时候将会看到。

这样,消息的循环过程就分析完了,这部分逻辑还是比较复杂的,它利用Linux系统中的管道(pipe)进程间通信机制来实现消息的等待和处理,不过,了解了这部分内容之后,下面我们分析消息的发送和处理就简单多了。

2. 消息的发送
应用程序的主线程准备就好消息队列并且进入到消息循环后,其它地方就可以往这个消息队列中发送消息了。我们继续以文章开始介绍的Android应用程序启动过程源代码分析一文中的应用程序启动过为例,说明应用程序是如何把消息加入到应用程序的消息队列中去的。

在Android应用程序启动过程源代码分析这篇文章的Step 30中,ActivityManagerService通过调用ApplicationThread类的scheduleLaunchActivity函数通知应用程序,它可以加载应用程序的默认Activity了,这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plain copy
  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalclassApplicationThreadextendsApplicationThreadNative{
  4. ......
  5. //weusetokentoidentifythisactivitywithouthavingtosendthe
  6. //activityitselfbacktotheactivitymanager.(mattersmorewithipc)
  7. publicfinalvoidscheduleLaunchActivity(Intentintent,IBindertoken,intident,
  8. ActivityInfoinfo,Bundlestate,List<ResultInfo>pendingResults,
  9. List<Intent>pendingNewIntents,booleannotResumed,booleanisForward){
  10. ActivityClientRecordr=newActivityClientRecord();
  11. r.token=token;
  12. r.ident=ident;
  13. r.intent=intent;
  14. r.activityInfo=info;
  15. r.state=state;
  16. r.pendingResults=pendingResults;
  17. r.pendingIntents=pendingNewIntents;
  18. r.startsNotResumed=notResumed;
  19. r.isForward=isForward;
  20. queueOrSendMessage(H.LAUNCH_ACTIVITY,r);
  21. }
  22. ......
  23. }
  24. ......
  25. }
这里把相关的参数都封装成一个ActivityClientRecord对象r,然后调用queueOrSendMessage函数来往应用程序的消息队列中加入一个新的消息(H.LAUNCH_ACTIVITY),这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plain copy
  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalclassApplicationThreadextendsApplicationThreadNative{
  4. ......
  5. //ifthethreadhasn'tstartedyet,wedon'thavethehandler,sojust
  6. //savethemessagesuntilwe'reready.
  7. privatefinalvoidqueueOrSendMessage(intwhat,Objectobj){
  8. queueOrSendMessage(what,obj,0,0);
  9. }
  10. ......
  11. privatefinalvoidqueueOrSendMessage(intwhat,Objectobj,intarg1,intarg2){
  12. synchronized(this){
  13. ......
  14. Messagemsg=Message.obtain();
  15. msg.what=what;
  16. msg.obj=obj;
  17. msg.arg1=arg1;
  18. msg.arg2=arg2;
  19. mH.sendMessage(msg);
  20. }
  21. }
  22. ......
  23. }
  24. ......
  25. }
在queueOrSendMessage函数中,又进一步把上面传进来的参数封装成一个Message对象msg,然后通过mH.sendMessage函数把这个消息对象msg加入到应用程序的消息队列中去。这里的mH是ActivityThread类的成员变量,它的类型为H,继承于Handler类,它定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plain copy
  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalclassHextendsHandler{
  4. ......
  5. publicvoidhandleMessage(Messagemsg){
  6. ......
  7. switch(msg.what){
  8. ......
  9. }
  10. ......
  11. }
  12. ......
  13. }

这个H类就是通过其成员函数handleMessage函数来处理消息的了,后面我们分析消息的处理过程时会看到。
ActivityThread类的这个mH成员变量是什么时候创建的呢?我们前面在分析应用程序的消息循环时,说到当应用程序进程启动之后,就会加载ActivityThread类的main函数里面,在这个main函数里面,在通过Looper类进入消息循环之前,会在当前进程中创建一个ActivityThread实例:

[java] view plain copy
  1. publicfinalclassActivityThread{
  2. ......
  3. publicstaticfinalvoidmain(String[]args){
  4. ......
  5. ActivityThreadthread=newActivityThread();
  6. thread.attach(false);
  7. ......
  8. }
  9. }
在创建这个实例的时候,就会同时创建其成员变量mH了:

[java] view plain copy
  1. publicfinalclassActivityThread{
  2. ......
  3. finalHmH=newH();
  4. ......
  5. }
前面说过,H类继承于Handler类,因此,当创建这个H对象时,会调用Handler类的构造函数,这个函数定义在frameworks/base/core/java/android/os/Handler.java文件中:

[java] view plain copy
  1. publicclassHandler{
  2. ......
  3. publicHandler(){
  4. ......
  5. mLooper=Looper.myLooper();
  6. ......
  7. mQueue=mLooper.mQueue;
  8. ......
  9. }
  10. finalMessageQueuemQueue;
  11. finalLoopermLooper;
  12. ......
  13. }
在Hanlder类的构造函数中,主要就是初始成员变量mLooper和mQueue了。这里的myLooper是Looper类的静态成员函数,通过它来获得一个Looper对象,这个Looper对象就是前面我们在分析消息循环时,在ActivityThread类的main函数中通过Looper.prepareMainLooper函数创建的。Looper.myLooper函数实现在frameworks/base/core/java/android/os/Looper.java文件中:

[java] view plain copy
  1. publicclassLooper{
  2. ......
  3. publicstaticfinalLoopermyLooper(){
  4. return(Looper)sThreadLocal.get();
  5. }
  6. ......
  7. }
有了这个Looper对象后,就可以通过Looper.mQueue来访问应用程序的消息队列了。

有了这个Handler对象mH后,就可以通过它来往应用程序的消息队列中加入新的消息了。回到前面的queueOrSendMessage函数中,当它准备好了一个Message对象msg后,就开始调用mH.sendMessage函数来发送消息了,这个函数定义在frameworks/base/core/java/android/os/Handler.java文件中:

[java] view plain copy
  1. publicclassHandler{
  2. ......
  3. publicfinalbooleansendMessage(Messagemsg)
  4. {
  5. returnsendMessageDelayed(msg,0);
  6. }
  7. publicfinalbooleansendMessageDelayed(Messagemsg,longdelayMillis)
  8. {
  9. if(delayMillis<0){
  10. delayMillis=0;
  11. }
  12. returnsendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);
  13. }
  14. publicbooleansendMessageAtTime(Messagemsg,longuptimeMillis)
  15. {
  16. booleansent=false;
  17. MessageQueuequeue=mQueue;
  18. if(queue!=null){
  19. msg.target=this;
  20. sent=queue.enqueueMessage(msg,uptimeMillis);
  21. }
  22. else{
  23. ......
  24. }
  25. returnsent;
  26. }
  27. ......
  28. }
在发送消息时,是可以指定消息的处理时间的,但是通过sendMessage函数发送的消息的处理时间默认就为当前时间,即表示要马上处理,因此,从sendMessage函数中调用sendMessageDelayed函数,传入的时间参数为0,表示这个消息不要延时处理,而在sendMessageDelayed函数中,则会先获得当前时间,然后加上消息要延时处理的时间,即得到这个处理这个消息的绝对时间,然后调用sendMessageAtTime函数来把消息加入到应用程序的消息队列中去。

在sendMessageAtTime函数,首先得到应用程序的消息队列mQueue,这是在Handler对象构造时初始化好的,前面已经分析过了,接着设置这个消息的目标对象target,即这个消息最终是由谁来处理的:

[java] view plain copy
  1. msg.target=this;
这里将它赋值为this,即表示这个消息最终由这个Handler对象来处理,即由ActivityThread对象的mH成员变量来处理。

函数最后调用queue.enqueueMessage来把这个消息加入到应用程序的消息队列中去,这个函数实现在frameworks/base/core/java/android/os/MessageQueue.java文件中:

[java] view plain copy
  1. publicclassMessageQueue{
  2. ......
  3. finalbooleanenqueueMessage(Messagemsg,longwhen){
  4. ......
  5. finalbooleanneedWake;
  6. synchronized(this){
  7. ......
  8. msg.when=when;
  9. //Log.d("MessageQueue","Enqueing:"+msg);
  10. Messagep=mMessages;
  11. if(p==null||when==0||when<p.when){
  12. msg.next=p;
  13. mMessages=msg;
  14. needWake=mBlocked;//newhead,mightneedtowakeup
  15. }else{
  16. Messageprev=null;
  17. while(p!=null&&p.when<=when){
  18. prev=p;
  19. p=p.next;
  20. }
  21. msg.next=prev.next;
  22. prev.next=msg;
  23. needWake=false;//stillwaitingonhead,noneedtowakeup
  24. }
  25. }
  26. if(needWake){
  27. nativeWake(mPtr);
  28. }
  29. returntrue;
  30. }
  31. ......
  32. }
把消息加入到消息队列时,分两种情况,一种当前消息队列为空时,这时候应用程序的主线程一般就是处于空闲等待状态了,这时候就要唤醒它,另一种情况是应用程序的消息队列不为空,这时候就不需要唤醒应用程序的主线程了,因为这时候它一定是在忙着处于消息队列中的消息,因此不会处于空闲等待的状态。

第一种情况比较简单,只要把消息放在消息队列头就可以了:

[java] view plain copy
  1. msg.next=p;
  2. mMessages=msg;
  3. needWake=mBlocked;//newhead,mightneedtowakeup
第二种情况相对就比较复杂一些了,前面我们说过,当往消息队列中发送消息时,是可以指定消息的处理时间的,而消息队列中的消息,就是按照这个时间从小到大来排序的,因此,当把新的消息加入到消息队列时,就要根据它的处理时间来找到合适的位置,然后再放进消息队列中去:

[java] view plain copy
  1. Messageprev=null;
  2. while(p!=null&&p.when<=when){
  3. prev=p;
  4. p=p.next;
  5. }
  6. msg.next=prev.next;
  7. prev.next=msg;
  8. needWake=false;//stillwaitingonhead,noneedtowakeup
把消息加入到消息队列去后,如果应用程序的主线程正处于空闲等待状态,就需要调用natvieWake函数来唤醒它了,这是一个JNI方法,定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:

[java] view plain copy
  1. staticvoidandroid_os_MessageQueue_nativeWake(JNIEnv*env,jobjectobj,jintptr){
  2. NativeMessageQueue*nativeMessageQueue=reinterpret_cast<NativeMessageQueue*>(ptr);
  3. returnnativeMessageQueue->wake();
  4. }
这个JNI层的NativeMessageQueue对象我们在前面分析消息循环的时候创建好的,保存在Java层的MessageQueue对象的mPtr成员变量中,这里把它取回来之后,就调用它的wake函数来唤醒应用程序的主线程,这个函数也是定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中:

[java] view plain copy
  1. voidNativeMessageQueue::wake(){
  2. mLooper->wake();
  3. }
这里它又通过成员变量mLooper的wake函数来执行操作,这里的mLooper成员变量是一个C++层实现的Looper对象,它定义在frameworks/base/libs/utils/Looper.cpp文件中:

[java] view plain copy
  1. voidLooper::wake(){
  2. ......
  3. ssize_tnWrite;
  4. do{
  5. nWrite=write(mWakeWritePipeFd,"W",1);
  6. }while(nWrite==-1&&errno==EINTR);
  7. .......
  8. }
这个wake函数很简单,只是通过打开文件描述符mWakeWritePipeFd往管道的写入一个"W"字符串。其实,往管道写入什么内容并不重要,往管道写入内容的目的是为了唤醒应用程序的主线程。前面我们在分析应用程序的消息循环时说到,当应用程序的消息队列中没有消息处理时,应用程序的主线程就会进入空闲等待状态,而这个空闲等待状态就是通过调用这个Looper类的pollInner函数来进入的,具体就是在pollInner函数中调用epoll_wait函数来等待管道中有内容可读的。

这时候既然管道中有内容可读了,应用程序的主线程就会从这里的Looper类的pollInner函数返回到JNI层的nativePollOnce函数,最后返回到Java层中的MessageQueue.next函数中去,这里它就会发现消息队列中有新的消息需要处理了,于就会处理这个消息。

3. 消息的处理

前面在分析消息循环时,说到应用程序的主线程是在Looper类的loop成员函数中进行消息循环过程的,这个函数定义在frameworks/base/core/java/android/os/Looper.java文件中:

[java] view plain copy
  1. publicclassLooper{
  2. ......
  3. publicstaticfinalvoidloop(){
  4. Looperme=myLooper();
  5. MessageQueuequeue=me.mQueue;
  6. ......
  7. while(true){
  8. Messagemsg=queue.next();//mightblock
  9. ......
  10. if(msg!=null){
  11. if(msg.target==null){
  12. //Notargetisamagicidentifierforthequitmessage.
  13. return;
  14. }
  15. ......
  16. msg.target.dispatchMessage(msg);
  17. ......
  18. msg.recycle();
  19. }
  20. }
  21. }
  22. ......
  23. }
它从消息队列中获得消息对象msg后,就会调用它的target成员变量的dispatchMessage函数来处理这个消息。在前面分析消息的发送时说过,这个消息对象msg的成员变量target是在发送消息的时候设置好的,一般就通过哪个Handler来发送消息,就通过哪个Handler来处理消息。

我们继续以前面分析消息的发送时所举的例子来分析消息的处理过程。前面说到,在Android应用程序启动过程源代码分析这篇文章的Step 30中,ActivityManagerService通过调用ApplicationThread类的scheduleLaunchActivity函数通知应用程序,它可以加载应用程序的默认Activity了,而ApplicationThread类的scheduleLaunchActivity函数最终把这个请求封装成一个消息,然后通过ActivityThread类的成员变量mH来把这个消息加入到应用程序的消息队列中去。现在要对这个消息进行处理了,于是就会调用H类的dispatchMessage函数进行处理。

H类没有实现自己的dispatchMessage函数,但是它继承了父类Handler的dispatchMessage函数,这个函数定义在frameworks/base/core/java/android/os/ Handler.java文件中:

[java] view plain copy
  1. publicclassHandler{
  2. ......
  3. publicvoiddispatchMessage(Messagemsg){
  4. if(msg.callback!=null){
  5. handleCallback(msg);
  6. }else{
  7. if(mCallback!=null){
  8. if(mCallback.handleMessage(msg)){
  9. return;
  10. }
  11. }
  12. handleMessage(msg);
  13. }
  14. }
  15. ......
  16. }
这里的消息对象msg的callback成员变量和Handler类的mCallBack成员变量一般都为null,于是,就会调用Handler类的handleMessage函数来处理这个消息,由于H类在继承Handler类时,重写了handleMessage函数,因此,这里调用的实际上是H类的handleMessage函数,这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

[java] view plain copy
  1. publicfinalclassActivityThread{
  2. ......
  3. privatefinalclassHextendsHandler{
  4. ......
  5. publicvoidhandleMessage(Messagemsg){
  6. ......
  7. switch(msg.what){
  8. caseLAUNCH_ACTIVITY:{
  9. ActivityClientRecordr=(ActivityClientRecord)msg.obj;
  10. r.packageInfo=getPackageInfoNoCheck(
  11. r.activityInfo.applicationInfo);
  12. handleLaunchActivity(r,null);
  13. }break;
  14. ......
  15. }
  16. ......
  17. }
  18. ......
  19. }
因为前面在分析消息的发送时所举的例子中,发送的消息的类型为H.LAUNCH_ACTIVITY,因此,这里就会调用ActivityThread类的handleLaunchActivity函数来真正地处理这个消息了,后面的具体过程就可以参考 Android应用程序启动过程源代码分析 这篇文章了。

至此,我们就从消息循环、消息发送和消息处理三个部分分析完Android应用程序的消息处理机制了,为了更深理解,这里我们对其中的一些要点作一个总结:

A. Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的。

B. Android应用程序的主线程在进入消息循环过程前,会在内部创建一个Linux管道(Pipe),这个管道的作用是使得Android应用程序主线程在消息队列为空时可以进入空闲等待状态,并且使得当应用程序的消息队列有消息需要处理时唤醒应用程序的主线程。

C. Android应用程序的主线程进入空闲等待状态的方式实际上就是在管道的读端等待管道中有新的内容可读,具体来说就是是通过Linux系统的Epoll机制中的epoll_wait函数进行的。

D. 当往Android应用程序的消息队列中加入新的消息时,会同时往管道中的写端写入内容,通过这种方式就可以唤醒正在等待消息到来的应用程序主线程。

E. 当应用程序主线程在进入空闲等待前,会认为当前线程处理空闲状态,于是就会调用那些已经注册了的IdleHandler接口,使得应用程序有机会在空闲的时候处理一些事情。





android的消息处理机制(图+源码分析)——Looper,Handler,Message


作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想。android源码中包含了大量的设计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了。这不,前几天为了了解android的消息处理机制,我看了Looper,Handler,Message这几个类的源码,结果又一次被googler的设计震撼了,特与大家分享。

android的消息处理有三个核心类:Looper,Handler和Message。其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因此我没将其作为核心类。下面一一介绍:

线程的魔法师 Looper

Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。使用Looper类创建Looper线程很简单:

public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();

// ...其他处理,如实例化handler

// 开始循环处理消息队列
Looper.loop();
}
}

通过上面两行核心代码,你的线程就升级为Looper线程了!!!是不是很神奇?让我们放慢镜头,看看这两行代码各自做了什么。

1)Looper.prepare()

通过上图可以看到,现在你的线程中有一个Looper对象,它的内部维护了一个消息队列MQ。注意,一个Thread只能有一个Looper对象,为什么呢?咱们来看源码。

public class Looper {
// 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
private static final ThreadLocal sThreadLocal = new ThreadLocal();
// Looper内的消息队列
final MessageQueue mQueue;
// 当前线程
Thread mThread;
// 。。。其他属性

// 每个Looper对象中有它的消息队列,和它所属的线程
private Looper() {
mQueue
= new MessageQueue();
mRun
= true;
mThread
= Thread.currentThread();
}

// 我们调用该方法会在调用线程的TLS中创建Looper对象
public static final void prepare() {
if (sThreadLocal.get() != null) {
// 试图在有Looper的线程中再次创建Looper将抛出异常
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(
new Looper());
}
// 其他方法
}

通过源码,prepare()背后的工作方式一目了然,其核心就是将looper对象定义为ThreadLocal。如果你还不清楚什么是ThreadLocal,请参考《理解ThreadLocal》。

2)Looper.loop()

调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。其源码分析如下:

  public static final void loop() {
Looper me
= myLooper(); //得到当前线程Looper
MessageQueue queue = me.mQueue; //得到当前looper的MQ

// 这两行没看懂= = 不过不影响理解
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 开始循环
while (true) {
Message msg
= queue.next(); // 取出message
if (msg != null) {
if (msg.target == null) {
// message没有target为结束信号,退出循环
return;
}
// 日志。。。
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
// 非常重要!将真正的处理工作交给message的target,即后面要讲的handler
msg.target.dispatchMessage(msg);
// 还是日志。。。
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);

// 下面没看懂,同样不影响理解
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);
}
// 回收message资源
msg.recycle();
}
}
}

除了prepare()和loop()方法,Looper类还提供了一些有用的方法,比如

Looper.myLooper()得到当前线程looper对象:

    public static final Looper myLooper() {
// 在任意线程调用Looper.myLooper()返回的都是那个线程的looper
return (Looper)sThreadLocal.get();
}

getThread()得到looper对象所属线程:

    public Thread getThread() {
return mThread;
}

quit()方法结束looper循环:

    public void quit() {
// 创建一个空的message,它的target为NULL,表示结束循环消息
Message msg = Message.obtain();
// 发出消息
mQueue.enqueueMessage(msg, 0);
}

到此为止,你应该对Looper有了基本的了解,总结几点:

1.每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal

2.Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行

3.Looper使一个线程变成Looper线程。

那么,我们如何往MQ上添加消息呢?下面有请Handler!(掌声~~~)

异步处理大师 Handler

什么是handler?handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的。默认的构造方法:

public class handler {

final MessageQueue mQueue; // 关联的MQ
final Looper mLooper; // 关联的looper
final Callback mCallback;
// 其他属性

public Handler() {
// 没看懂,直接略过,,,
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers()
& Modifier.STATIC) == 0) {
Log.w(TAG,
"The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
// 默认将关联当前线程的looper
mLooper = Looper.myLooper();
// looper不能为空,即该默认的构造方法只能在looper线程中使用
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
mQueue = mLooper.mQueue;
mCallback
= null;
}

// 其他方法
}

下面我们就可以为之前的LooperThread类加入Handler:

public class LooperThread extends Thread {
private Handler handler1;
private Handler handler2;

@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();

// 实例化两个handler
handler1 = new Handler();
handler2
= new Handler();

// 开始循环处理消息队列
Looper.loop();
}
}

加入handler后的效果如下图:

可以看到,一个线程可以有多个Handler,但是只能有一个Looper!

Handler发送消息

有了handler之后,我们就可以使用post(Runnable),postAtTime(Runnable, long),postDelayed(Runnable, long),sendEmptyMessage(int),sendMessage(Message),sendMessageAtTime(Message, long)sendMessageDelayed(Message, long)这些方法向MQ上发送消息了。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,见源码:

    // 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行
public final boolean post(Runnable r)
{
// 注意getPostMessage(r)将runnable封装成message
return sendMessageDelayed(getPostMessage(r), 0);
}

private final Message getPostMessage(Runnable r) {
Message m
= Message.obtain(); //得到空的message
m.callback = r; //将runnable设为message的callback,
return m;
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue
= mQueue;
if (queue != null) {
msg.target
= this; // message的target必须设为该handler!
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e
= new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w(
"Looper", e.getMessage(), e);
}
return sent;
}

其他方法就不罗列了,总之通过handler发出的message有如下特点:

1.message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码

msg.target.dispatchMessage(msg);

2.post发出的message,其callback为Runnable对象

Handler处理消息

说完了消息的发送,再来看下handler如何处理消息。消息的处理是通过核心方法dispatchMessage(Messagemsg)与钩子方法handleMessage(Messagemsg)完成的,见源码

    // 处理消息,该方法由looper调用
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 如果message设置了callback,即runnable消息,处理callback!
handleCallback(msg);
}
else {
// 如果handler本身设置了callback,则执行callback
if (mCallback != null) {
/* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */
if (mCallback.handleMessage(msg)) {
return;
}
}
// 如果message没有callback,则调用handler的钩子方法handleMessage
handleMessage(msg);
}
}

// 处理runnable消息
private final void handleCallback(Message message) {
message.callback.run();
//直接调用run方法!
}
// 由子类实现的钩子方法
public void handleMessage(Message msg) {
}

可以看到,除了handleMessage(Messagemsg)和Runnable对象的run方法由开发者实现外(实现具体逻辑),handler的内部工作机制对开发者是透明的。这正是handler API设计的精妙之处!

Handler的用处

我在小标题中将handler描述为“异步处理大师”,这归功于Handler拥有下面两个重要的特点:

1.handler可以在任意线程发送消息,这些消息会被添加到关联的MQ上。

2.handler是在它关联的looper线程中处理消息的。

这就解决了android最经典的不能在其他非主线程中更新UI的问题。android的主线程也是一个looper线程(looper在android中运用很广),我们在其中创建的handler默认将关联主线程MQ。因此,利用handler的一个solution就是在activity中创建handler并将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知activity更新UI。(过程如图)

下面给出sample代码,仅供参考:

public class TestDriverActivity extends Activity {
private TextView textview;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textview
= (TextView) findViewById(R.id.textview);
// 创建并启动工作线程
Thread workerThread = new Thread(new SampleTask(new MyHandler()));
workerThread.start();
}

public void appendText(String msg) {
textview.setText(textview.getText()
+ "\n" + msg);
}

class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
String result
= msg.getData().getString("message");
// 更新UI
appendText(result);
}
}
}
public class SampleTask implements Runnable {
private static final String TAG = SampleTask.class.getSimpleName();
Handler handler;

public SampleTask(Handler handler) {
super();
this.handler = handler;
}

@Override
public void run() {
try { // 模拟执行某项任务,下载等
Thread.sleep(5000);
// 任务完成后通知activity更新UI
Message msg = prepareMessage("task completed!");
// message将被添加到主线程的MQ中
handler.sendMessage(msg);
}
catch (InterruptedException e) {
Log.d(TAG,
"interrupted!");
}

}

private Message prepareMessage(String str) {
Message result
= handler.obtainMessage();
Bundle data
= new Bundle();
data.putString(
"message", str);
result.setData(data);
return result;
}

}

当然,handler能做的远远不仅如此,由于它能post Runnable对象,它还能与Looper配合实现经典的Pipeline Thread(流水线线程)模式。请参考此文《Android Guts: Intro to Loopers and Handlers》

封装任务 Message

在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,这里不做总结了。但是有这么几点需要注意(待补充):

1.尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。

2.如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存

3.擅用message.what来标识信息,以便用不同方式处理message。




AsyncTask详解与应用一


AsyncTask 能够让你恰当容易地使用UI线程。AsyncTask其实是Android给开发者提供的一个简单轻量级的多线程类,通过它我们可以很容易新建一个线程做一些耗时的操作,并在这个过程中更新UI。之所以说它轻量级,是因为缺少了直接使用Thread的灵活性。这个类允许执行后台操作,在UI线程上发布的结果而无需操纵线程或Handler。AsyncTask设计出来的目的就是作为Thread和Handler的一个辅助类,并不构成一个通用线程框架。asynctasks应用于短作业(最多几秒钟)。如果你需要保持线程运行很长一段时间,那么强烈建议你使用javaAPIjava.util.concurrent包里面的类,例如Executor, ThreadPoolExecutor and FutureTask。一个AsyncTask任务由计算运行在后台线程上,其结果发表在UI线程上。它有三种参数类型, Params, Progress and Result和四个步骤:onPreExecute, doInBackground, onProgressUpdate and onPostExecute。

AsyncTask必须使用子类,也就是必须继承 AsyncTask才能使用它。子类会覆盖至少一个方法(doInBackground(Params…)),通常将覆盖第二个(onPostExecute(Result))。
下面看一个例子:

[java] view plain copy
  1. privateclassDownloadFilesTaskextendsAsyncTask<URL,Integer,Long>{
  2. protectedLongdoInBackground(URL...urls){
  3. intcount=urls.length;
  4. longtotalSize=0;
  5. for(inti=0;i<count;i++){
  6. totalSize+=Downloader.downloadFile(urls[i]);
  7. publishProgress((int)((i/(float)count)*100));
  8. //Escapeearlyifcancel()iscalled
  9. if(isCancelled())break;
  10. }
  11. returntotalSize;
  12. }
  13. protectedvoidonProgressUpdate(Integer...progress){
  14. setProgressPercent(progress[0]);
  15. }
  16. protectedvoidonPostExecute(Longresult){
  17. showDialog("Downloaded"+result+"bytes");
  18. }
  19. }
一旦创建,执行一个任务非常简单:
[java] view plain copy
  1. newDownloadFilesTask().execute(url1,url2,url3);
android的类AsyncTask对线程间通讯进行了包装,提供了简易的编程方式来使后台线程和UI线程进行通讯:后台线程执行异步任务,并把操作结果通知UI线程。
AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result。
Params 启动任务执行的输入参数,比如HTTP请求的URL。
Progress 后台任务执行的百分比。
Result 后台执行任务最终返回的结果,比如String,Integer。

AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,开发者需要实现这些方法。
1) 继承AsyncTask
2) 实现AsyncTask中定义的下面一个或几个方法
onPreExecute(), 该方法将在执行实际的后台操作前被UI 线程调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条,或者一些控件的实例化,这个方法可以不用实现。
doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台处理工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
onProgressUpdate(Progress...),在publishProgress方法被调用后,UI 线程将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI 线程调用,后台的计算结果将通过该方法传递到UI 线程,并且在界面上展示给用户.
onCancelled(),在用户取消线程操作的时候调用。在主线程中调用onCancelled()的时候调用。
为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
   1) Task的实例必须在UI 线程中创建
   2) execute方法必须在UI 线程中调用
  3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法,需要在UI线程中实例化这个task来调用。
   4) 该task只能被执行一次,否则多次调用时将会出现异常

doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。阅读AsyncTask的源码可知,AsyncTask是使用java.util.concurrent 框架来管理线程以及任务的执行的。


实例

1、一个模拟下载进度条的例子

[java] view plain copy
  1. packagecom.example.asynctaskdemo;
  2. importandroid.app.Activity;
  3. importandroid.os.AsyncTask;
  4. importandroid.os.Bundle;
  5. importandroid.view.View;
  6. importandroid.widget.Button;
  7. importandroid.widget.ProgressBar;
  8. importandroid.widget.TextView;
  9. publicclassAsy1ActivityextendsActivity{
  10. Buttondownload;
  11. ProgressBarpb;
  12. TextViewtv;
  13. /**Calledwhentheactivityisfirstcreated.*/
  14. @Override
  15. publicvoidonCreate(BundlesavedInstanceState){
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.asy1);
  18. pb=(ProgressBar)findViewById(R.id.pb);
  19. tv=(TextView)findViewById(R.id.tv);
  20. download=(Button)findViewById(R.id.download);
  21. download.setOnClickListener(newView.OnClickListener(){
  22. @Override
  23. publicvoidonClick(Viewv){
  24. DownloadTasktask=newDownloadTask();
  25. task.execute(100);
  26. }
  27. });
  28. }
  29. classDownloadTaskextendsAsyncTask<Integer,Integer,String>{
  30. @Override
  31. protectedvoidonCancelled(){
  32. //TODOAuto-generatedmethodstub
  33. super.onCancelled();
  34. }
  35. @Override
  36. protectedvoidonPostExecute(Stringresult){
  37. setTitle(result);
  38. super.onPostExecute(result);
  39. }
  40. @Override
  41. protectedvoidonPreExecute(){
  42. //TODOAuto-generatedmethodstub
  43. super.onPreExecute();
  44. }
  45. @Override
  46. protectedvoidonProgressUpdate(Integer...values){
  47. //TODOAuto-generatedmethodstub
  48. super.onProgressUpdate(values);
  49. tv.setText(values[0]+"%");
  50. }
  51. @Override
  52. protectedStringdoInBackground(Integer...params){
  53. for(inti=0;i<=100;i++){
  54. pb.setProgress(i);
  55. publishProgress(i);
  56. try{
  57. Thread.sleep(params[0]);
  58. }catch(InterruptedExceptione){
  59. e.printStackTrace();
  60. }
  61. }
  62. return"执行完毕";
  63. }
  64. }
  65. }

1、一个从网络下载图片的例子

[java] view plain copy
  1. packagecom.example.asynctaskdemo;
  2. importjava.io.ByteArrayOutputStream;
  3. importjava.io.IOException;
  4. importjava.io.InputStream;
  5. importjava.net.HttpURLConnection;
  6. importjava.net.MalformedURLException;
  7. importjava.net.URL;
  8. importandroid.app.Activity;
  9. importandroid.graphics.Bitmap;
  10. importandroid.graphics.BitmapFactory;
  11. importandroid.os.AsyncTask;
  12. importandroid.os.Bundle;
  13. importandroid.view.View;
  14. importandroid.view.View.OnClickListener;
  15. importandroid.widget.Button;
  16. importandroid.widget.ImageView;
  17. importandroid.widget.ProgressBar;
  18. publicclassAsy2ActivityextendsActivity{
  19. privateButtonbutton;
  20. privateProgressBarprogressBar;
  21. privateImageViewimageView;
  22. privatefinalStringimageUrl="http://avatar.csdn.net/D/1/4/1_wangjinyu501.jpg";
  23. @Override
  24. protectedvoidonCreate(BundlesavedInstanceState){
  25. //TODOAuto-generatedmethodstub
  26. super.onCreate(savedInstanceState);
  27. setContentView(R.layout.asy2);
  28. initView();
  29. }
  30. privatevoidinitView(){
  31. button=(Button)findViewById(R.id.button);
  32. button.setOnClickListener(newOnClickListener(){
  33. @Override
  34. publicvoidonClick(Viewarg0){
  35. AsyncTaskLoadImageasyncTaskLoadImage=newAsyncTaskLoadImage();
  36. asyncTaskLoadImage.execute(imageUrl);
  37. }
  38. });
  39. progressBar=(ProgressBar)findViewById(R.id.pb);
  40. imageView=(ImageView)findViewById(R.id.imageview);
  41. }
  42. classAsyncTaskLoadImageextendsAsyncTask<String,Integer,Bitmap>{
  43. @Override
  44. protectedvoidonPreExecute(){
  45. super.onPreExecute();
  46. }
  47. @Override
  48. protectedBitmapdoInBackground(String...params){
  49. Bitmapbitmap=null;
  50. try{
  51. URLurl=newURL(params[0]);
  52. HttpURLConnectionurlConnection=(HttpURLConnection)url
  53. .openConnection();
  54. urlConnection.connect();
  55. intMAX=urlConnection.getContentLength();
  56. progressBar.setMax(MAX);
  57. InputStreaminputStream=urlConnection.getInputStream();
  58. ByteArrayOutputStreambyteArrayOutputStream=newByteArrayOutputStream();
  59. byte[]b=newbyte[1024];
  60. intlen=0;
  61. intprocessBarNum=0;
  62. while((len=inputStream.read(b))!=-1){
  63. byteArrayOutputStream.write(b,0,len);
  64. processBarNum+=len;
  65. publishProgress(processBarNum);
  66. }
  67. //bitmap=BitmapFactory.decodeStream(inputStream);
  68. bitmap=BitmapFactory.decodeByteArray(
  69. byteArrayOutputStream.toByteArray(),0,MAX);
  70. inputStream.close();
  71. }catch(MalformedURLExceptione){
  72. e.printStackTrace();
  73. }catch(IOExceptione){
  74. e.printStackTrace();
  75. }
  76. returnbitmap;
  77. }
  78. @Override
  79. protectedvoidonProgressUpdate(Integer...values){
  80. progressBar.setProgress(values[0]);
  81. super.onProgressUpdate(values);
  82. }
  83. @Override
  84. protectedvoidonPostExecute(Bitmapresult){
  85. imageView.setImageBitmap(result);
  86. super.onPostExecute(result);
  87. }
  88. }
  89. }

Android之Looper、Handler、Message、MessageQueue应用篇


简介

上一篇文章介绍了Handler、Message、MessageQueue等Android线程交互方面的内容,Android之理解Looper、Handler、Message、MessageQueue。下面开始实践,学习如何去使用以及应用到程序里面。

实例

在这里使用ListView作为异步下载图片的环境。

1、Handle+Runnable

实现思路是:

[java] view plain copy
  1. packagecom.example.handlerloadiage;
  2. importjava.io.IOException;
  3. importjava.net.URL;
  4. importandroid.app.Activity;
  5. importandroid.graphics.drawable.Drawable;
  6. importandroid.os.Bundle;
  7. importandroid.os.Handler;
  8. importandroid.util.Log;
  9. importandroid.view.LayoutInflater;
  10. importandroid.view.Menu;
  11. importandroid.view.View;
  12. importandroid.view.ViewGroup;
  13. importandroid.widget.BaseAdapter;
  14. importandroid.widget.ImageView;
  15. importandroid.widget.ListView;
  16. publicclassHandler_Runnable_ModeextendsActivity{
  17. privateListViewlistview;
  18. @Override
  19. protectedvoidonCreate(BundlesavedInstanceState){
  20. super.onCreate(savedInstanceState);
  21. setContentView(R.layout.activity_handlerimageloader);
  22. listview=(ListView)findViewById(R.id.listview);
  23. listview.setAdapter(newMyAdapter());
  24. }
  25. privateclassMyAdapterextendsBaseAdapter{
  26. publicMyAdapter(){
  27. }
  28. @Override
  29. publicintgetCount(){
  30. //TODOAuto-generatedmethodstub
  31. return100;
  32. }
  33. @Override
  34. publicObjectgetItem(intposition){
  35. //TODOAuto-generatedmethodstub
  36. returnnull;
  37. }
  38. @Override
  39. publiclonggetItemId(intposition){
  40. //TODOAuto-generatedmethodstub
  41. return0;
  42. }
  43. @Override
  44. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  45. if(convertView==null){
  46. convertView=LayoutInflater.from(getApplicationContext())
  47. .inflate(R.layout.list_item,null);
  48. }
  49. finalImageViewimage=(ImageView)convertView
  50. .findViewById(R.id.imageview);
  51. finalStringimageURL="http://avatar.csdn.net/D/1/4/3_wangjinyu501.jpg";
  52. Handlerhandler=newHandler();
  53. handler.post(newRunnable(){
  54. publicvoidrun(){
  55. Drawabledrawable=null;
  56. try{
  57. drawable=Drawable.createFromStream(
  58. newURL(imageURL).openStream(),"image.jpg");
  59. }catch(IOExceptione){
  60. Log.d("test",e.getMessage());
  61. }
  62. if(drawable==null){
  63. Log.d("test","nulldrawable");
  64. }else{
  65. Log.d("test","notnulldrawable");
  66. }
  67. if(drawable==null){
  68. image.setImageResource(R.drawable.ic_launcher);
  69. }else{
  70. image.setImageDrawable(drawable);
  71. }
  72. }
  73. });
  74. returnconvertView;
  75. }
  76. }
  77. @Override
  78. publicbooleanonCreateOptionsMenu(Menumenu){
  79. getMenuInflater().inflate(R.menu.main,menu);
  80. returntrue;
  81. }
  82. }
效果如下:

快速滑动的过程中,还是出现了ANR的现象。



这是因为handler.post(new Runnable()这个方法,并没有开启一个新的线程,他还是在UI主线程中,所以导致出现ANR现象。


2、Handler+Runnable+Message

实现思路:

1:在UI线程中启动一个线程,让这个线程去下载图片。

2:图片完成下载后发送一个消息去通知UI线程

3:UI线程获取到消息后,更新UI。

实现代码:

[java] view plain copy
  1. packagecom.example.handlerloadiage;
  2. importjava.io.IOException;
  3. importjava.net.URL;
  4. importandroid.app.Activity;
  5. importandroid.graphics.drawable.Drawable;
  6. importandroid.os.Bundle;
  7. importandroid.os.Handler;
  8. importandroid.os.Message;
  9. importandroid.util.Log;
  10. importandroid.view.LayoutInflater;
  11. importandroid.view.Menu;
  12. importandroid.view.View;
  13. importandroid.view.ViewGroup;
  14. importandroid.widget.BaseAdapter;
  15. importandroid.widget.ImageView;
  16. importandroid.widget.ListView;
  17. publicclassHandler_Runnable_ModeextendsActivity{
  18. privateListViewlistview;
  19. @Override
  20. protectedvoidonCreate(BundlesavedInstanceState){
  21. super.onCreate(savedInstanceState);
  22. setContentView(R.layout.activity_handlerimageloader);
  23. listview=(ListView)findViewById(R.id.listview);
  24. listview.setAdapter(newMyAdapter());
  25. }
  26. privateclassMyAdapterextendsBaseAdapter{
  27. publicMyAdapter(){
  28. }
  29. @Override
  30. publicintgetCount(){
  31. //TODOAuto-generatedmethodstub
  32. return100;
  33. }
  34. @Override
  35. publicObjectgetItem(intposition){
  36. //TODOAuto-generatedmethodstub
  37. returnnull;
  38. }
  39. @Override
  40. publiclonggetItemId(intposition){
  41. //TODOAuto-generatedmethodstub
  42. return0;
  43. }
  44. @Override
  45. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  46. if(convertView==null){
  47. convertView=LayoutInflater.from(getApplicationContext())
  48. .inflate(R.layout.list_item,null);
  49. }
  50. finalImageViewimage=(ImageView)convertView
  51. .findViewById(R.id.imageview);
  52. finalStringimageURL="http://avatar.csdn.net/D/1/4/3_wangjinyu501.jpg";
  53. finalHandlerhandler=newHandler(){
  54. @Override
  55. publicvoidhandleMessage(Messagemsg){
  56. super.handleMessage(msg);
  57. Drawabled=(Drawable)msg.obj;
  58. if(d==null){
  59. image.setImageResource(R.drawable.ic_launcher);
  60. }else{
  61. image.setImageDrawable(d);
  62. }
  63. }
  64. };
  65. handler.post(newRunnable(){
  66. publicvoidrun(){
  67. Drawabledrawable=null;
  68. try{
  69. drawable=Drawable.createFromStream(
  70. newURL(imageURL).openStream(),"image.jpg");
  71. Messagemessage=handler.obtainMessage();
  72. message.obj=drawable;
  73. handler.sendMessage(message);
  74. }catch(IOExceptione){
  75. Log.d("test",e.getMessage());
  76. }
  77. if(drawable==null){
  78. Log.d("test","nulldrawable");
  79. }else{
  80. Log.d("test","notnulldrawable");
  81. }
  82. }
  83. });
  84. returnconvertView;
  85. }
  86. }
  87. @Override
  88. publicbooleanonCreateOptionsMenu(Menumenu){
  89. getMenuInflater().inflate(R.menu.main,menu);
  90. returntrue;
  91. }
  92. }
也是会出现ANR的现象,原因和之前一样。


3、Handler+Thread+Message

这种模式使用了线程,所以是异步加载。

[java] view plain copy
  1. packagecom.example.handlerloadiage;
  2. importjava.io.IOException;
  3. importjava.net.URL;
  4. importandroid.app.Activity;

更多相关文章

  1. Android应用程序消息处理机制
  2. Android(安卓)服务器推送技术
  3. Android推送通知指南
  4. Android(安卓)消息处理 -- Looper 、Handler类
  5. 你不知道的Runnable接口,深度解析Runnable接口
  6. Android的消息机制(java层)
  7. 简单解释Android中的任务、进程和线程
  8. android使用mysql的方法总结
  9. Invalidate和postInvalidate的区别

随机推荐

  1. Android RecyclerView DividerItemDecora
  2. ch010 Android GridView
  3. Android Email程序源码
  4. android内存机制
  5. Android(安卓)Jetpack系列——ViewModel
  6. Android ContentProvider
  7. Android: Android 3.0 SDK发布,速度更新之
  8. Android Bitmap内存优化
  9. android interview questions
  10. Android sharedUserId