Android中的AsyncTask和Handler应用实例二
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
- publicfinalclassActivityThread{
- ......
- publicstaticfinalvoidmain(String[]args){
- ......
- Looper.prepareMainLooper();
- ......
- ActivityThreadthread=newActivityThread();
- thread.attach(false);
- ......
- Looper.loop();
- ......
- thread.detach();
- ......
- }
- }
首先看Looper.prepareMainLooper函数的实现,这是一个静态成员函数,定义在frameworks/base/core/java/android/os/Looper.java文件中:
[java] view plain copy
- publicclassLooper{
- ......
- privatestaticfinalThreadLocalsThreadLocal=newThreadLocal();
- finalMessageQueuemQueue;
- ......
- /**Initializethecurrentthreadasalooper.
- *Thisgivesyouachancetocreatehandlersthatthenreference
- *thislooper,beforeactuallystartingtheloop.Besuretocall
- *{@link#loop()}aftercallingthismethod,andenditbycalling
- *{@link#quit()}.
- */
- publicstaticfinalvoidprepare(){
- if(sThreadLocal.get()!=null){
- thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread");
- }
- sThreadLocal.set(newLooper());
- }
- /**Initializethecurrentthreadasalooper,markingitasanapplication'smain
- *looper.ThemainlooperforyourapplicationiscreatedbytheAndroidenvironment,
- *soyoushouldneverneedtocallthisfunctionyourself.
- *{@link#prepare()}
- */
- publicstaticfinalvoidprepareMainLooper(){
- prepare();
- setMainLooper(myLooper());
- if(Process.supportsProcesses()){
- myLooper().mQueue.mQuitAllowed=false;
- }
- }
- privatesynchronizedstaticvoidsetMainLooper(Looperlooper){
- mMainLooper=looper;
- }
- /**
- *ReturntheLooperobjectassociatedwiththecurrentthread.Returns
- *nullifthecallingthreadisnotassociatedwithaLooper.
- */
- publicstaticfinalLoopermyLooper(){
- return(Looper)sThreadLocal.get();
- }
- privateLooper(){
- mQueue=newMessageQueue();
- mRun=true;
- mThread=Thread.currentThread();
- }
- ......
- }
[java] view plain copy
- publicclassMessageQueue{
- ......
- privateintmPtr;//usedbynativecode
- privatenativevoidnativeInit();
- MessageQueue(){
- nativeInit();
- }
- ......
- }
[cpp] view plain copy
- staticvoidandroid_os_MessageQueue_nativeInit(JNIEnv*env,jobjectobj){
- NativeMessageQueue*nativeMessageQueue=newNativeMessageQueue();
- if(!nativeMessageQueue){
- jniThrowRuntimeException(env,"Unabletoallocatenativequeue");
- return;
- }
- android_os_MessageQueue_setNativeMessageQueue(env,obj,nativeMessageQueue);
- }
[cpp] view plain copy
- NativeMessageQueue::NativeMessageQueue(){
- mLooper=Looper::getForThread();
- if(mLooper==NULL){
- mLooper=newLooper(false);
- Looper::setForThread(mLooper);
- }
- }
这个Looper的创建过程也很重要,不过我们暂时放一放,先分析完android_os_MessageQueue_nativeInit函数的执行,它创建了本地消息队列NativeMessageQueue对象之后,接着调用android_os_MessageQueue_setNativeMessageQueue函数来把这个消息队列对象保存在前面我们在Java层中创建的MessageQueue对象的mPtr成员变量里面:
[cpp] view plain copy
- staticvoidandroid_os_MessageQueue_setNativeMessageQueue(JNIEnv*env,jobjectmessageQueueObj,
- NativeMessageQueue*nativeMessageQueue){
- env->SetIntField(messageQueueObj,gMessageQueueClassInfo.mPtr,
- reinterpret_cast<jint>(nativeMessageQueue));
- }
我们再回到NativeMessageQueue的构造函数中,看看JNI层的Looper对象的创建过程,即看看它的构造函数是如何实现的,这个Looper类实现在frameworks/base/libs/utils/Looper.cpp文件中:
[cpp] view plain copy
- Looper::Looper(boolallowNonCallbacks):
- mAllowNonCallbacks(allowNonCallbacks),
- mResponseIndex(0){
- intwakeFds[2];
- intresult=pipe(wakeFds);
- ......
- mWakeReadPipeFd=wakeFds[0];
- mWakeWritePipeFd=wakeFds[1];
- ......
- #ifdefLOOPER_USES_EPOLL
- //Allocatetheepollinstanceandregisterthewakepipe.
- mEpollFd=epoll_create(EPOLL_SIZE_HINT);
- ......
- structepoll_eventeventItem;
- memset(&eventItem,0,sizeof(epoll_event));//zerooutunusedmembersofdatafieldunion
- eventItem.events=EPOLLIN;
- eventItem.data.fd=mWakeReadPipeFd;
- result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);
- ......
- #else
- ......
- #endif
- ......
- }
[cpp] view plain copy
- intwakeFds[2];
- intresult=pipe(wakeFds);
- ......
- mWakeReadPipeFd=wakeFds[0];
- mWakeWritePipeFd=wakeFds[1];
要使用Linux系统的epoll机制,首先要通过epoll_create来创建一个epoll专用的文件描述符:
[cpp] view plain copy
- mEpollFd=epoll_create(EPOLL_SIZE_HINT);
接着还要通过epoll_ctl函数来告诉epoll要监控相应的文件描述符的什么事件:
[cpp] view plain copy
- structepoll_eventeventItem;
- memset(&eventItem,0,sizeof(epoll_event));//zerooutunusedmembersofdatafieldunion
- eventItem.events=EPOLLIN;
- eventItem.data.fd=mWakeReadPipeFd;
- result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);
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
- publicclassLooper{
- ......
- publicstaticfinalvoidloop(){
- Looperme=myLooper();
- MessageQueuequeue=me.mQueue;
- ......
- while(true){
- Messagemsg=queue.next();//mightblock
- ......
- if(msg!=null){
- if(msg.target==null){
- //Notargetisamagicidentifierforthequitmessage.
- return;
- }
- ......
- msg.target.dispatchMessage(msg);
- ......
- msg.recycle();
- }
- }
- }
- ......
- }
这个函数最关键的地方便是从消息队列中获取下一个要处理的消息了,即MessageQueue.next函数,它实现frameworks/base/core/java/android/os/MessageQueue.java文件中:
[java] view plain copy
- publicclassMessageQueue{
- ......
- finalMessagenext(){
- intpendingIdleHandlerCount=-1;//-1onlyduringfirstiteration
- intnextPollTimeoutMillis=0;
- for(;;){
- if(nextPollTimeoutMillis!=0){
- Binder.flushPendingCommands();
- }
- nativePollOnce(mPtr,nextPollTimeoutMillis);
- synchronized(this){
- //Trytoretrievethenextmessage.Returniffound.
- finallongnow=SystemClock.uptimeMillis();
- finalMessagemsg=mMessages;
- if(msg!=null){
- finallongwhen=msg.when;
- if(now>=when){
- mBlocked=false;
- mMessages=msg.next;
- msg.next=null;
- if(Config.LOGV)Log.v("MessageQueue","Returningmessage:"+msg);
- returnmsg;
- }else{
- nextPollTimeoutMillis=(int)Math.min(when-now,Integer.MAX_VALUE);
- }
- }else{
- nextPollTimeoutMillis=-1;
- }
- //Iffirsttime,thengetthenumberofidlerstorun.
- if(pendingIdleHandlerCount<0){
- pendingIdleHandlerCount=mIdleHandlers.size();
- }
- if(pendingIdleHandlerCount==0){
- //Noidlehandlerstorun.Loopandwaitsomemore.
- mBlocked=true;
- continue;
- }
- if(mPendingIdleHandlers==null){
- mPendingIdleHandlers=newIdleHandler[Math.max(pendingIdleHandlerCount,4)];
- }
- mPendingIdleHandlers=mIdleHandlers.toArray(mPendingIdleHandlers);
- }
- //Runtheidlehandlers.
- //Weonlyeverreachthiscodeblockduringthefirstiteration.
- for(inti=0;i<pendingIdleHandlerCount;i++){
- finalIdleHandleridler=mPendingIdleHandlers[i];
- mPendingIdleHandlers[i]=null;//releasethereferencetothehandler
- booleankeep=false;
- try{
- keep=idler.queueIdle();
- }catch(Throwablet){
- Log.wtf("MessageQueue","IdleHandlerthrewexception",t);
- }
- if(!keep){
- synchronized(this){
- mIdleHandlers.remove(idler);
- }
- }
- }
- //Resettheidlehandlercountto0sowedonotrunthemagain.
- pendingIdleHandlerCount=0;
- //Whilecallinganidlehandler,anewmessagecouldhavebeendelivered
- //sogobackandlookagainforapendingmessagewithoutwaiting.
- nextPollTimeoutMillis=0;
- }
- }
- ......
- }
执行下面语句是看看当前消息队列中有没有消息:
[java] view plain copy
- nativePollOnce(mPtr,nextPollTimeoutMillis);
当前nativePollOnce返回后,就去看看消息队列中有没有消息:
[java] view plain copy
- finalMessagemsg=mMessages;
- if(msg!=null){
- finallongwhen=msg.when;
- if(now>=when){
- mBlocked=false;
- mMessages=msg.next;
- msg.next=null;
- if(Config.LOGV)Log.v("MessageQueue","Returningmessage:"+msg);
- returnmsg;
- }else{
- nextPollTimeoutMillis=(int)Math.min(when-now,Integer.MAX_VALUE);
- }
- }else{
- nextPollTimeoutMillis=-1;
- }
[java] view plain copy
- nextPollTimeoutMillis=(int)Math.min(when-now,Integer.MAX_VALUE);
[java] view plain copy
- nextPollTimeoutMillis=-1;
这里计算出来的等待时间都是在下次调用nativePollOnce时使用的。
这里说的等待,是空闲等待,而不是忙等待,因此,在进入空闲等待状态前,如果应用程序注册了IdleHandler接口来处理一些事情,那么就会先执行这里IdleHandler,然后再进入等待状态。IdlerHandler是定义在MessageQueue的一个内部类:
[java] view plain copy
- publicclassMessageQueue{
- ......
- /**
- *Callbackinterfacefordiscoveringwhenathreadisgoingtoblock
- *waitingformoremessages.
- */
- publicstaticinterfaceIdleHandler{
- /**
- *Calledwhenthemessagequeuehasrunoutofmessagesandwillnow
- *waitformore.Returntruetokeepyouridlehandleractive,false
- *tohaveitremoved.Thismaybecallediftherearestillmessages
- *pendinginthequeue,buttheyareallscheduledtobedispatched
- *afterthecurrenttime.
- */
- booleanqueueIdle();
- }
- ......
- }
回到MessageQueue函数中,它接下来就是在进入等待状态前,看看有没有IdleHandler是需要执行的:
- //Iffirsttime,thengetthenumberofidlerstorun.
- if(pendingIdleHandlerCount<0){
- pendingIdleHandlerCount=mIdleHandlers.size();
- }
- if(pendingIdleHandlerCount==0){
- //Noidlehandlerstorun.Loopandwaitsomemore.
- mBlocked=true;
- continue;
- }
- if(mPendingIdleHandlers==null){
- mPendingIdleHandlers=newIdleHandler[Math.max(pendingIdleHandlerCount,4)];
- }
- mPendingIdleHandlers=mIdleHandlers.toArray(mPendingIdleHandlers);
接下来就是执行这些注册了的IdleHanlder了:
[java] view plain copy
- //Runtheidlehandlers.
- //Weonlyeverreachthiscodeblockduringthefirstiteration.
- for(inti=0;i<pendingIdleHandlerCount;i++){
- finalIdleHandleridler=mPendingIdleHandlers[i];
- mPendingIdleHandlers[i]=null;//releasethereferencetothehandler
- booleankeep=false;
- try{
- keep=idler.queueIdle();
- }catch(Throwablet){
- Log.wtf("MessageQueue","IdleHandlerthrewexception",t);
- }
- if(!keep){
- synchronized(this){
- mIdleHandlers.remove(idler);
- }
- }
- }
[java] view plain copy
- //Whilecallinganidlehandler,anewmessagecouldhavebeendelivered
- //sogobackandlookagainforapendingmessagewithoutwaiting.
- nextPollTimeoutMillis=0;
[cpp] view plain copy
- staticvoidandroid_os_MessageQueue_nativePollOnce(JNIEnv*env,jobjectobj,
- jintptr,jinttimeoutMillis){
- NativeMessageQueue*nativeMessageQueue=reinterpret_cast<NativeMessageQueue*>(ptr);
- nativeMessageQueue->pollOnce(timeoutMillis);
- }
[cpp] view plain copy
- voidNativeMessageQueue::pollOnce(inttimeoutMillis){
- mLooper->pollOnce(timeoutMillis);
- }
[cpp] view plain copy
- intLooper::pollOnce(inttimeoutMillis,int*outFd,int*outEvents,void**outData){
- intresult=0;
- for(;;){
- ......
- if(result!=0){
- ......
- returnresult;
- }
- result=pollInner(timeoutMillis);
- }
- }
函数pollInner的定义如下:
[cpp] view plain copy
- intLooper::pollInner(inttimeoutMillis){
- ......
- intresult=ALOOPER_POLL_WAKE;
- ......
- #ifdefLOOPER_USES_EPOLL
- structepoll_eventeventItems[EPOLL_MAX_EVENTS];
- inteventCount=epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis);
- boolacquiredLock=false;
- #else
- ......
- #endif
- if(eventCount<0){
- if(errno==EINTR){
- gotoDone;
- }
- LOGW("Pollfailedwithanunexpectederror,errno=%d",errno);
- result=ALOOPER_POLL_ERROR;
- gotoDone;
- }
- if(eventCount==0){
- ......
- result=ALOOPER_POLL_TIMEOUT;
- gotoDone;
- }
- ......
- #ifdefLOOPER_USES_EPOLL
- for(inti=0;i<eventCount;i++){
- intfd=eventItems[i].data.fd;
- uint32_tepollEvents=eventItems[i].events;
- if(fd==mWakeReadPipeFd){
- if(epollEvents&EPOLLIN){
- awoken();
- }else{
- LOGW("Ignoringunexpectedepollevents0x%xonwakereadpipe.",epollEvents);
- }
- }else{
- ......
- }
- }
- if(acquiredLock){
- mLock.unlock();
- }
- Done:;
- #else
- ......
- #endif
- ......
- returnresult;
- }
[cpp] view plain copy
- inteventCount=epoll_wait(mEpollFd,eventItems,EPOLL_MAX_EVENTS,timeoutMillis);
当mEpollFd所监控的文件描述符发生了要监控的IO事件后或者监控时间超时后,线程就从epoll_wait返回了,否则线程就会在epoll_wait函数中进入睡眠状态了。返回后如果eventCount等于0,就说明是超时了:
[cpp] view plain copy
- if(eventCount==0){
- ......
- result=ALOOPER_POLL_TIMEOUT;
- gotoDone;
- }
[cpp] view plain copy
- for(inti=0;i<eventCount;i++){
- intfd=eventItems[i].data.fd;
- uint32_tepollEvents=eventItems[i].events;
- if(fd==mWakeReadPipeFd){
- if(epollEvents&EPOLLIN){
- awoken();
- }else{
- LOGW("Ignoringunexpectedepollevents0x%xonwakereadpipe.",epollEvents);
- }
- }else{
- ......
- }
- }
函数awoken的实现很简单,它只是把管道中的内容都读取出来:
[cpp] view plain copy
- voidLooper::awoken(){
- ......
- charbuffer[16];
- ssize_tnRead;
- do{
- nRead=read(mWakeReadPipeFd,buffer,sizeof(buffer));
- }while((nRead==-1&&errno==EINTR)||nRead==sizeof(buffer));
- }
这样,消息的循环过程就分析完了,这部分逻辑还是比较复杂的,它利用Linux系统中的管道(pipe)进程间通信机制来实现消息的等待和处理,不过,了解了这部分内容之后,下面我们分析消息的发送和处理就简单多了。
2. 消息的发送
应用程序的主线程准备就好消息队列并且进入到消息循环后,其它地方就可以往这个消息队列中发送消息了。我们继续以文章开始介绍的Android应用程序启动过程源代码分析一文中的应用程序启动过为例,说明应用程序是如何把消息加入到应用程序的消息队列中去的。
在Android应用程序启动过程源代码分析这篇文章的Step 30中,ActivityManagerService通过调用ApplicationThread类的scheduleLaunchActivity函数通知应用程序,它可以加载应用程序的默认Activity了,这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:
[java] view plain copy
- publicfinalclassActivityThread{
- ......
- privatefinalclassApplicationThreadextendsApplicationThreadNative{
- ......
- //weusetokentoidentifythisactivitywithouthavingtosendthe
- //activityitselfbacktotheactivitymanager.(mattersmorewithipc)
- publicfinalvoidscheduleLaunchActivity(Intentintent,IBindertoken,intident,
- ActivityInfoinfo,Bundlestate,List<ResultInfo>pendingResults,
- List<Intent>pendingNewIntents,booleannotResumed,booleanisForward){
- ActivityClientRecordr=newActivityClientRecord();
- r.token=token;
- r.ident=ident;
- r.intent=intent;
- r.activityInfo=info;
- r.state=state;
- r.pendingResults=pendingResults;
- r.pendingIntents=pendingNewIntents;
- r.startsNotResumed=notResumed;
- r.isForward=isForward;
- queueOrSendMessage(H.LAUNCH_ACTIVITY,r);
- }
- ......
- }
- ......
- }
[java] view plain copy
- publicfinalclassActivityThread{
- ......
- privatefinalclassApplicationThreadextendsApplicationThreadNative{
- ......
- //ifthethreadhasn'tstartedyet,wedon'thavethehandler,sojust
- //savethemessagesuntilwe'reready.
- privatefinalvoidqueueOrSendMessage(intwhat,Objectobj){
- queueOrSendMessage(what,obj,0,0);
- }
- ......
- privatefinalvoidqueueOrSendMessage(intwhat,Objectobj,intarg1,intarg2){
- synchronized(this){
- ......
- Messagemsg=Message.obtain();
- msg.what=what;
- msg.obj=obj;
- msg.arg1=arg1;
- msg.arg2=arg2;
- mH.sendMessage(msg);
- }
- }
- ......
- }
- ......
- }
[java] view plain copy
- publicfinalclassActivityThread{
- ......
- privatefinalclassHextendsHandler{
- ......
- publicvoidhandleMessage(Messagemsg){
- ......
- switch(msg.what){
- ......
- }
- ......
- }
- ......
- }
这个H类就是通过其成员函数handleMessage函数来处理消息的了,后面我们分析消息的处理过程时会看到。
ActivityThread类的这个mH成员变量是什么时候创建的呢?我们前面在分析应用程序的消息循环时,说到当应用程序进程启动之后,就会加载ActivityThread类的main函数里面,在这个main函数里面,在通过Looper类进入消息循环之前,会在当前进程中创建一个ActivityThread实例:
[java] view plain copy
- publicfinalclassActivityThread{
- ......
- publicstaticfinalvoidmain(String[]args){
- ......
- ActivityThreadthread=newActivityThread();
- thread.attach(false);
- ......
- }
- }
[java] view plain copy
- publicfinalclassActivityThread{
- ......
- finalHmH=newH();
- ......
- }
[java] view plain copy
- publicclassHandler{
- ......
- publicHandler(){
- ......
- mLooper=Looper.myLooper();
- ......
- mQueue=mLooper.mQueue;
- ......
- }
- finalMessageQueuemQueue;
- finalLoopermLooper;
- ......
- }
[java] view plain copy
- publicclassLooper{
- ......
- publicstaticfinalLoopermyLooper(){
- return(Looper)sThreadLocal.get();
- }
- ......
- }
有了这个Handler对象mH后,就可以通过它来往应用程序的消息队列中加入新的消息了。回到前面的queueOrSendMessage函数中,当它准备好了一个Message对象msg后,就开始调用mH.sendMessage函数来发送消息了,这个函数定义在frameworks/base/core/java/android/os/Handler.java文件中:
[java] view plain copy
- publicclassHandler{
- ......
- publicfinalbooleansendMessage(Messagemsg)
- {
- returnsendMessageDelayed(msg,0);
- }
- publicfinalbooleansendMessageDelayed(Messagemsg,longdelayMillis)
- {
- if(delayMillis<0){
- delayMillis=0;
- }
- returnsendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);
- }
- publicbooleansendMessageAtTime(Messagemsg,longuptimeMillis)
- {
- booleansent=false;
- MessageQueuequeue=mQueue;
- if(queue!=null){
- msg.target=this;
- sent=queue.enqueueMessage(msg,uptimeMillis);
- }
- else{
- ......
- }
- returnsent;
- }
- ......
- }
在sendMessageAtTime函数,首先得到应用程序的消息队列mQueue,这是在Handler对象构造时初始化好的,前面已经分析过了,接着设置这个消息的目标对象target,即这个消息最终是由谁来处理的:
[java] view plain copy
- msg.target=this;
函数最后调用queue.enqueueMessage来把这个消息加入到应用程序的消息队列中去,这个函数实现在frameworks/base/core/java/android/os/MessageQueue.java文件中:
- publicclassMessageQueue{
- ......
- finalbooleanenqueueMessage(Messagemsg,longwhen){
- ......
- finalbooleanneedWake;
- synchronized(this){
- ......
- msg.when=when;
- //Log.d("MessageQueue","Enqueing:"+msg);
- Messagep=mMessages;
- if(p==null||when==0||when<p.when){
- msg.next=p;
- mMessages=msg;
- needWake=mBlocked;//newhead,mightneedtowakeup
- }else{
- Messageprev=null;
- while(p!=null&&p.when<=when){
- prev=p;
- p=p.next;
- }
- msg.next=prev.next;
- prev.next=msg;
- needWake=false;//stillwaitingonhead,noneedtowakeup
- }
- }
- if(needWake){
- nativeWake(mPtr);
- }
- returntrue;
- }
- ......
- }
第一种情况比较简单,只要把消息放在消息队列头就可以了:
[java] view plain copy
- msg.next=p;
- mMessages=msg;
- needWake=mBlocked;//newhead,mightneedtowakeup
[java] view plain copy
- Messageprev=null;
- while(p!=null&&p.when<=when){
- prev=p;
- p=p.next;
- }
- msg.next=prev.next;
- prev.next=msg;
- needWake=false;//stillwaitingonhead,noneedtowakeup
[java] view plain copy
- staticvoidandroid_os_MessageQueue_nativeWake(JNIEnv*env,jobjectobj,jintptr){
- NativeMessageQueue*nativeMessageQueue=reinterpret_cast<NativeMessageQueue*>(ptr);
- returnnativeMessageQueue->wake();
- }
[java] view plain copy
- voidNativeMessageQueue::wake(){
- mLooper->wake();
- }
[java] view plain copy
- voidLooper::wake(){
- ......
- ssize_tnWrite;
- do{
- nWrite=write(mWakeWritePipeFd,"W",1);
- }while(nWrite==-1&&errno==EINTR);
- .......
- }
这时候既然管道中有内容可读了,应用程序的主线程就会从这里的Looper类的pollInner函数返回到JNI层的nativePollOnce函数,最后返回到Java层中的MessageQueue.next函数中去,这里它就会发现消息队列中有新的消息需要处理了,于就会处理这个消息。
3. 消息的处理
前面在分析消息循环时,说到应用程序的主线程是在Looper类的loop成员函数中进行消息循环过程的,这个函数定义在frameworks/base/core/java/android/os/Looper.java文件中:
[java] view plain copy
- publicclassLooper{
- ......
- publicstaticfinalvoidloop(){
- Looperme=myLooper();
- MessageQueuequeue=me.mQueue;
- ......
- while(true){
- Messagemsg=queue.next();//mightblock
- ......
- if(msg!=null){
- if(msg.target==null){
- //Notargetisamagicidentifierforthequitmessage.
- return;
- }
- ......
- msg.target.dispatchMessage(msg);
- ......
- msg.recycle();
- }
- }
- }
- ......
- }
我们继续以前面分析消息的发送时所举的例子来分析消息的处理过程。前面说到,在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
- publicclassHandler{
- ......
- publicvoiddispatchMessage(Messagemsg){
- if(msg.callback!=null){
- handleCallback(msg);
- }else{
- if(mCallback!=null){
- if(mCallback.handleMessage(msg)){
- return;
- }
- }
- handleMessage(msg);
- }
- }
- ......
- }
[java] view plain copy
- publicfinalclassActivityThread{
- ......
- privatefinalclassHextendsHandler{
- ......
- publicvoidhandleMessage(Messagemsg){
- ......
- switch(msg.what){
- caseLAUNCH_ACTIVITY:{
- ActivityClientRecordr=(ActivityClientRecord)msg.obj;
- r.packageInfo=getPackageInfoNoCheck(
- r.activityInfo.applicationInfo);
- handleLaunchActivity(r,null);
- }break;
- ......
- }
- ......
- }
- ......
- }
至此,我们就从消息循环、消息发送和消息处理三个部分分析完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
- privateclassDownloadFilesTaskextendsAsyncTask<URL,Integer,Long>{
- protectedLongdoInBackground(URL...urls){
- intcount=urls.length;
- longtotalSize=0;
- for(inti=0;i<count;i++){
- totalSize+=Downloader.downloadFile(urls[i]);
- publishProgress((int)((i/(float)count)*100));
- //Escapeearlyifcancel()iscalled
- if(isCancelled())break;
- }
- returntotalSize;
- }
- protectedvoidonProgressUpdate(Integer...progress){
- setProgressPercent(progress[0]);
- }
- protectedvoidonPostExecute(Longresult){
- showDialog("Downloaded"+result+"bytes");
- }
- }
[java] view plain copy
- newDownloadFilesTask().execute(url1,url2,url3);
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
- packagecom.example.asynctaskdemo;
- importandroid.app.Activity;
- importandroid.os.AsyncTask;
- importandroid.os.Bundle;
- importandroid.view.View;
- importandroid.widget.Button;
- importandroid.widget.ProgressBar;
- importandroid.widget.TextView;
- publicclassAsy1ActivityextendsActivity{
- Buttondownload;
- ProgressBarpb;
- TextViewtv;
- /**Calledwhentheactivityisfirstcreated.*/
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.asy1);
- pb=(ProgressBar)findViewById(R.id.pb);
- tv=(TextView)findViewById(R.id.tv);
- download=(Button)findViewById(R.id.download);
- download.setOnClickListener(newView.OnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- DownloadTasktask=newDownloadTask();
- task.execute(100);
- }
- });
- }
- classDownloadTaskextendsAsyncTask<Integer,Integer,String>{
- @Override
- protectedvoidonCancelled(){
- //TODOAuto-generatedmethodstub
- super.onCancelled();
- }
- @Override
- protectedvoidonPostExecute(Stringresult){
- setTitle(result);
- super.onPostExecute(result);
- }
- @Override
- protectedvoidonPreExecute(){
- //TODOAuto-generatedmethodstub
- super.onPreExecute();
- }
- @Override
- protectedvoidonProgressUpdate(Integer...values){
- //TODOAuto-generatedmethodstub
- super.onProgressUpdate(values);
- tv.setText(values[0]+"%");
- }
- @Override
- protectedStringdoInBackground(Integer...params){
- for(inti=0;i<=100;i++){
- pb.setProgress(i);
- publishProgress(i);
- try{
- Thread.sleep(params[0]);
- }catch(InterruptedExceptione){
- e.printStackTrace();
- }
- }
- return"执行完毕";
- }
- }
- }
1、一个从网络下载图片的例子
[java] view plain copy- packagecom.example.asynctaskdemo;
- importjava.io.ByteArrayOutputStream;
- importjava.io.IOException;
- importjava.io.InputStream;
- importjava.net.HttpURLConnection;
- importjava.net.MalformedURLException;
- importjava.net.URL;
- importandroid.app.Activity;
- importandroid.graphics.Bitmap;
- importandroid.graphics.BitmapFactory;
- importandroid.os.AsyncTask;
- importandroid.os.Bundle;
- importandroid.view.View;
- importandroid.view.View.OnClickListener;
- importandroid.widget.Button;
- importandroid.widget.ImageView;
- importandroid.widget.ProgressBar;
- publicclassAsy2ActivityextendsActivity{
- privateButtonbutton;
- privateProgressBarprogressBar;
- privateImageViewimageView;
- privatefinalStringimageUrl="http://avatar.csdn.net/D/1/4/1_wangjinyu501.jpg";
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- //TODOAuto-generatedmethodstub
- super.onCreate(savedInstanceState);
- setContentView(R.layout.asy2);
- initView();
- }
- privatevoidinitView(){
- button=(Button)findViewById(R.id.button);
- button.setOnClickListener(newOnClickListener(){
- @Override
- publicvoidonClick(Viewarg0){
- AsyncTaskLoadImageasyncTaskLoadImage=newAsyncTaskLoadImage();
- asyncTaskLoadImage.execute(imageUrl);
- }
- });
- progressBar=(ProgressBar)findViewById(R.id.pb);
- imageView=(ImageView)findViewById(R.id.imageview);
- }
- classAsyncTaskLoadImageextendsAsyncTask<String,Integer,Bitmap>{
- @Override
- protectedvoidonPreExecute(){
- super.onPreExecute();
- }
- @Override
- protectedBitmapdoInBackground(String...params){
- Bitmapbitmap=null;
- try{
- URLurl=newURL(params[0]);
- HttpURLConnectionurlConnection=(HttpURLConnection)url
- .openConnection();
- urlConnection.connect();
- intMAX=urlConnection.getContentLength();
- progressBar.setMax(MAX);
- InputStreaminputStream=urlConnection.getInputStream();
- ByteArrayOutputStreambyteArrayOutputStream=newByteArrayOutputStream();
- byte[]b=newbyte[1024];
- intlen=0;
- intprocessBarNum=0;
- while((len=inputStream.read(b))!=-1){
- byteArrayOutputStream.write(b,0,len);
- processBarNum+=len;
- publishProgress(processBarNum);
- }
- //bitmap=BitmapFactory.decodeStream(inputStream);
- bitmap=BitmapFactory.decodeByteArray(
- byteArrayOutputStream.toByteArray(),0,MAX);
- inputStream.close();
- }catch(MalformedURLExceptione){
- e.printStackTrace();
- }catch(IOExceptione){
- e.printStackTrace();
- }
- returnbitmap;
- }
- @Override
- protectedvoidonProgressUpdate(Integer...values){
- progressBar.setProgress(values[0]);
- super.onProgressUpdate(values);
- }
- @Override
- protectedvoidonPostExecute(Bitmapresult){
- imageView.setImageBitmap(result);
- super.onPostExecute(result);
- }
- }
- }
Android之Looper、Handler、Message、MessageQueue应用篇
简介
上一篇文章介绍了Handler、Message、MessageQueue等Android线程交互方面的内容,Android之理解Looper、Handler、Message、MessageQueue。下面开始实践,学习如何去使用以及应用到程序里面。
实例
在这里使用ListView作为异步下载图片的环境。
1、Handle+Runnable
实现思路是:
[java] view plain copy
- packagecom.example.handlerloadiage;
- importjava.io.IOException;
- importjava.net.URL;
- importandroid.app.Activity;
- importandroid.graphics.drawable.Drawable;
- importandroid.os.Bundle;
- importandroid.os.Handler;
- importandroid.util.Log;
- importandroid.view.LayoutInflater;
- importandroid.view.Menu;
- importandroid.view.View;
- importandroid.view.ViewGroup;
- importandroid.widget.BaseAdapter;
- importandroid.widget.ImageView;
- importandroid.widget.ListView;
- publicclassHandler_Runnable_ModeextendsActivity{
- privateListViewlistview;
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_handlerimageloader);
- listview=(ListView)findViewById(R.id.listview);
- listview.setAdapter(newMyAdapter());
- }
- privateclassMyAdapterextendsBaseAdapter{
- publicMyAdapter(){
- }
- @Override
- publicintgetCount(){
- //TODOAuto-generatedmethodstub
- return100;
- }
- @Override
- publicObjectgetItem(intposition){
- //TODOAuto-generatedmethodstub
- returnnull;
- }
- @Override
- publiclonggetItemId(intposition){
- //TODOAuto-generatedmethodstub
- return0;
- }
- @Override
- publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
- if(convertView==null){
- convertView=LayoutInflater.from(getApplicationContext())
- .inflate(R.layout.list_item,null);
- }
- finalImageViewimage=(ImageView)convertView
- .findViewById(R.id.imageview);
- finalStringimageURL="http://avatar.csdn.net/D/1/4/3_wangjinyu501.jpg";
- Handlerhandler=newHandler();
- handler.post(newRunnable(){
- publicvoidrun(){
- Drawabledrawable=null;
- try{
- drawable=Drawable.createFromStream(
- newURL(imageURL).openStream(),"image.jpg");
- }catch(IOExceptione){
- Log.d("test",e.getMessage());
- }
- if(drawable==null){
- Log.d("test","nulldrawable");
- }else{
- Log.d("test","notnulldrawable");
- }
- if(drawable==null){
- image.setImageResource(R.drawable.ic_launcher);
- }else{
- image.setImageDrawable(drawable);
- }
- }
- });
- returnconvertView;
- }
- }
- @Override
- publicbooleanonCreateOptionsMenu(Menumenu){
- getMenuInflater().inflate(R.menu.main,menu);
- returntrue;
- }
- }
快速滑动的过程中,还是出现了ANR的现象。
这是因为handler.post(new Runnable()这个方法,并没有开启一个新的线程,他还是在UI主线程中,所以导致出现ANR现象。
2、Handler+Runnable+Message
实现思路:
1:在UI线程中启动一个线程,让这个线程去下载图片。
2:图片完成下载后发送一个消息去通知UI线程
3:UI线程获取到消息后,更新UI。
实现代码:
[java] view plain copy
- packagecom.example.handlerloadiage;
- importjava.io.IOException;
- importjava.net.URL;
- importandroid.app.Activity;
- importandroid.graphics.drawable.Drawable;
- importandroid.os.Bundle;
- importandroid.os.Handler;
- importandroid.os.Message;
- importandroid.util.Log;
- importandroid.view.LayoutInflater;
- importandroid.view.Menu;
- importandroid.view.View;
- importandroid.view.ViewGroup;
- importandroid.widget.BaseAdapter;
- importandroid.widget.ImageView;
- importandroid.widget.ListView;
- publicclassHandler_Runnable_ModeextendsActivity{
- privateListViewlistview;
- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_handlerimageloader);
- listview=(ListView)findViewById(R.id.listview);
- listview.setAdapter(newMyAdapter());
- }
- privateclassMyAdapterextendsBaseAdapter{
- publicMyAdapter(){
- }
- @Override
- publicintgetCount(){
- //TODOAuto-generatedmethodstub
- return100;
- }
- @Override
- publicObjectgetItem(intposition){
- //TODOAuto-generatedmethodstub
- returnnull;
- }
- @Override
- publiclonggetItemId(intposition){
- //TODOAuto-generatedmethodstub
- return0;
- }
- @Override
- publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
- if(convertView==null){
- convertView=LayoutInflater.from(getApplicationContext())
- .inflate(R.layout.list_item,null);
- }
- finalImageViewimage=(ImageView)convertView
- .findViewById(R.id.imageview);
- finalStringimageURL="http://avatar.csdn.net/D/1/4/3_wangjinyu501.jpg";
- finalHandlerhandler=newHandler(){
- @Override
- publicvoidhandleMessage(Messagemsg){
- super.handleMessage(msg);
- Drawabled=(Drawable)msg.obj;
- if(d==null){
- image.setImageResource(R.drawable.ic_launcher);
- }else{
- image.setImageDrawable(d);
- }
- }
- };
- handler.post(newRunnable(){
- publicvoidrun(){
- Drawabledrawable=null;
- try{
- drawable=Drawable.createFromStream(
- newURL(imageURL).openStream(),"image.jpg");
- Messagemessage=handler.obtainMessage();
- message.obj=drawable;
- handler.sendMessage(message);
- }catch(IOExceptione){
- Log.d("test",e.getMessage());
- }
- if(drawable==null){
- Log.d("test","nulldrawable");
- }else{
- Log.d("test","notnulldrawable");
- }
- }
- });
- returnconvertView;
- }
- }
- @Override
- publicbooleanonCreateOptionsMenu(Menumenu){
- getMenuInflater().inflate(R.menu.main,menu);
- returntrue;
- }
- }
3、Handler+Thread+Message
这种模式使用了线程,所以是异步加载。
[java] view plain copy
- packagecom.example.handlerloadiage;
- importjava.io.IOException;
- importjava.net.URL;
- importandroid.app.Activity;
更多相关文章
- Android应用程序消息处理机制
- Android(安卓)服务器推送技术
- Android推送通知指南
- Android(安卓)消息处理 -- Looper 、Handler类
- 你不知道的Runnable接口,深度解析Runnable接口
- Android的消息机制(java层)
- 简单解释Android中的任务、进程和线程
- android使用mysql的方法总结
- Invalidate和postInvalidate的区别