首页 文章 精选 留言 我的

精选列表

搜索[记忆机制],共10000篇文章
优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(27)

Step 14.NativeInputQueue.unregisterInputChannel 这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中: status_tNativeInputQueue::unregisterInputChannel(JNIEnv*env,jobjectinputChannelObj){ sp<InputChannel>inputChannel=android_view_InputChannel_getInputChannel(env, inputChannelObj); ...... {//acquirelock AutoMutex_l(mLock); ssize_tconnectionIndex=getConnectionIndex(inputChannel); ...... sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex); mConnectionsByReceiveFd.removeItemsAt(connectionIndex); connection->status=Connection::STATUS_ZOMBIE; connection->looper->removeFd(inputChannel->getReceivePipeFd()); env->DeleteGlobalRef(connection->inputHandlerObjGlobal); connection->inputHandlerObjGlobal=NULL; ...... }//releaselock ...... returnOK; } 真正的注销工作就是这里实现的了,读者可以对照前面介绍应用程序注册键盘消息接收通道过程中的Step 21(NativeInputQueue.registerInputChannel)来分析,它首先是将在之前创建的Connection对象从NativeInputQueue中的mConnectionByReceiveFd向量中删除: ssize_tconnectionIndex=getConnectionIndex(inputChannel); ...... sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex); mConnectionsByReceiveFd.removeItemsAt(connectionIndex); 然后再把这个Client端InputChannel的前向管道的读端文件描述符从应用程序主线程中的Looper对象中删除: connection->looper->removeFd(inputChannel->getReceivePipeFd()); 这样,这个Activity窗口以后就不会接收到键盘事件了。 最后将Connection对象中的回调对象inputHandlerOjbGlobal对象删除: env->DeleteGlobalRef(connection->inputHandlerObjGlobal); connection->inputHandlerObjGlobal=NULL; 回忆一下前面我们在分析InputManager分发键盘消息给应用程序处理时,曾经说到,每当有键盘事件发生时,InputManager首先就会调用NativeInputQueue类的handleReceiveCallback函数。在这个handleReceiveCallback函数里面,NativeInputQueue会找到相应的Connection对象,然后把它里面的内部对象inputHandlerOjbGlobal作为参数来调用Java层的InputQueue类的dispatchKeyEvent函数来通知应用程序,有键盘事件发生了。在InputQueue类的dispatchKeyEvent函数里面,就是通过这个inputHandlerOjbGlobal对象来直正通知到当前激活的Activity窗口来处理这个键盘事件的。 注册在应用程序这一侧的Client端InputChannel被注销以后,回到前面的Step 11中,我们继续分析注销注册在InputManager这一侧的Server端InputChannel。Step 15. WindowManagerService.Session.remove 这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中: publicclassWindowManagerServiceextendsIWindowManager.Stub implementsWatchdog.Monitor{ ...... privatefinalclassSessionextendsIWindowSession.Stub implementsIBinder.DeathRecipient{ ...... publicvoidremove(IWindowwindow){ removeWindow(this,window); } ...... } ...... } 这个函数只是简单地调用其外部类WindowManagerService的removeWindow函数来进一步执行操作。 Step 16.WindowManagerService.removeWindow 这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中: publicclassWindowManagerServiceextendsIWindowManager.Stub implementsWatchdog.Monitor{ ...... publicvoidremoveWindow(Sessionsession,IWindowclient){ synchronized(mWindowMap){ WindowStatewin=windowForClientLocked(session,client,false); if(win==null){ return; } removeWindowLocked(session,win); } } ...... } 回忆一下前面我们在分析应用程序注册键盘消息管道的过程时,在Step 11(WindowManagerService.addWindow)中,WindowManagerService为这个即将要激活的Activity窗口创建了一个WindowState对象win,创建的时候,使用了从ViewRoot中传过来的两个参数,分别是一个Session对象session和一个IWindow对象client。 在这个函数中,ViewRoot传过来的两个参数session和client和上面说的两个参数是一致的,因此,这个函数首先通过参数session和client得到一个WindowState对象win,然后调用removeWindowLocked来把它从WindowManagerService删除。 Step 17.WindowManagerService.removeWindowLocked 这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中: publicclassWindowManagerServiceextendsIWindowManager.Stub implementsWatchdog.Monitor{ ...... publicvoidremoveWindowLocked(Sessionsession,WindowStatewin){ ...... win.disposeInputChannel(); ...... } ...... } 我们忽略了这个函数的其它逻辑,只关注注销之前注册的Server端InputChannel的逻辑,这里,注销的操作就是调用win的disposeInputChannel进行的了。 Step 18. WindowState.disposeInputChannel 这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中: publicclassWindowManagerServiceextendsIWindowManager.Stub implementsWatchdog.Monitor{ ...... privatefinalclassWindowStateimplementsWindowManagerPolicy.WindowState{ ...... voiddisposeInputChannel(){ if(mInputChannel!=null){ mInputManager.unregisterInputChannel(mInputChannel); mInputChannel.dispose(); mInputChannel=null; } } ...... } ...... } 上面说到,在前面分析应用程序注册键盘消息管道的过程时,在Step 11(WindowManagerService.addWindow)中,为当前这个Activity窗口创建了一个WindowState对象,接着创建了一个输入管道后,把Server端的InputChannel保存了在这个WindowState对象的成员变量mInputChannel中,因此,这里,就可以把它取回来,然后调用mInputManager对象的unregisterInputChannel函数来把它注销掉了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966658,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(25)

Step 2. ActivityStack.activityIdleInternal 这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中: publicclassActivityStack{ ...... finalvoidactivityIdleInternal(IBindertoken,booleanfromTimeout, Configurationconfig){ ...... ArrayList<ActivityRecord>stops=null; ...... intNS=0; ...... synchronized(mService){ ...... //Atomicallyretrievealloftheotherthingstodo. stops=processStoppingActivitiesLocked(true); NS=stops!=null?stops.size():0; ...... } inti; ...... //Stopanyactivitiesthatarescheduledtodosobuthavebeen //waitingforthenextonetostart. for(i=0;i<NS;i++){ ActivityRecordr=(ActivityRecord)stops.get(i); synchronized(mService){ if(r.finishing){ finishCurrentActivityLocked(r,FINISH_IMMEDIATELY); }else{ ...... } } } ...... } ...... } 这个函数首先会调用processStoppingActivitiesLocked函数把所有处于Stopped状态的Activity取回来,然后逐个分析它们,如果它们的ActivityRecord中的finishing成员变量为true,就说明这个Activity需要销毁了,于是,就调用finishCurrentActivityLocked函数来销毁它们。 Step 3.ActivityStack.finishCurrentActivityLocked 这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中: publicclassActivityStack{ ...... privatefinalActivityRecordfinishCurrentActivityLocked(ActivityRecordr, intmode){ ...... returnfinishCurrentActivityLocked(r,index,mode); } privatefinalActivityRecordfinishCurrentActivityLocked(ActivityRecordr, intindex,intmode){ ...... //makesuretherecordiscleanedoutofotherplaces. mStoppingActivities.remove(r); mWaitingVisibleActivities.remove(r); ...... finalActivityStateprevState=r.state; r.state=ActivityState.FINISHING; if(mode==FINISH_IMMEDIATELY ||prevState==ActivityState.STOPPED ||prevState==ActivityState.INITIALIZING){ //Ifthisactivityisalreadystopped,wecanjustfinish //itrightnow. returndestroyActivityLocked(r,true)?null:r; }else{ ...... } returnr; } ...... } 从上面的Step 2中传进来的参数mode为FINISH_IMMEDIATELY,并且这个即将要被销毁的Activity的状态为Stopped,因此,接下来就会调用destroyActivityLocked函数来销毁它。 Step 4.ActivityStack.destroyActivityLocked 这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中: publicclassActivityStack{ ...... finalbooleandestroyActivityLocked(ActivityRecordr, booleanremoveFromApp){ ...... booleanremovedFromHistory=false; ...... finalbooleanhadApp=r.app!=null; if(hadApp){ ...... try{ ...... r.app.thread.scheduleDestroyActivity(r,r.finishing, r.configChangeFlags); }catch(Exceptione){ ...... } ...... }else{ ...... } ...... returnremovedFromHistory; } ...... } 在前面一篇文章Android应用程序启动过程源代码分析中,我们说到,每一个应用程序进程在ActivityManagerService中,都ProcessRecord记录与之对应,而每一个Activity,都是运行在一个进程上下文中,因此,在ActivityManagerService中,每一个ActivityRecord的app成员变量都应该指向一个ProcessRecord记录,于是,这里得到的hadApp为true。在ProcessRecord类中,有一个成员变量thread,它的类型为IApplicationThread。在文章Android应用程序启动过程源代码分析中,我们也曾经说过,每一个应用程序在启动的时候,它都会在内部创建一个ActivityThread对象,而在这个ActivityThread对象中,有一个成员变量mAppThread,它的类型为ApplicationThread,这是一个Binder对象,专门用来负责在应用程序和ActivityManagerService之间执行进程间通信工作的。应用程序在启动的时候,就会将这个Binder对象传递给ActivityManagerService,而ActivityManagerService就会把它保存在相应的ProcessRecord记录的thread成员变量中。因此,ProcessRecord记录的thread成员变量其实就是ApplicationThread对象的远程接口,于是,执行下面这个语句的时候: r.app.thread.scheduleDestroyActivity(r,r.finishing, r.configChangeFlags); 就会进入到ApplicationThread类中的scheduleDestroyActivity函数来。 Step 5.ApplicationThread.scheduleDestroyActivity 这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中: publicfinalclassActivityThread{ ...... privatefinalclassApplicationThreadextendsApplicationThreadNative{ ...... publicfinalvoidscheduleDestroyActivity(IBindertoken,booleanfinishing, intconfigChanges){ queueOrSendMessage(H.DESTROY_ACTIVITY,token,finishing?1:0, configChanges); } ...... } ...... } 这个函数调用外部类ActivityThread的queueOrSendMessage函数来往应用程序的消息队列中发送一个H.DESTROY_ACTIVITY消息,这个消息最终由ActivityThread类的handleDestroyActivity函数来处理。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966656,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(26)

Step 7.LocalWindowManager.removeViewImmediate 这个函数定义在frameworks/base/core/java/android/view/Window.java文件中: publicabstractclassWindow{ ...... privateclassLocalWindowManagerimplementsWindowManager{ ...... publicfinalvoidremoveViewImmediate(Viewview){ mWindowManager.removeViewImmediate(view); } ...... privatefinalWindowManagermWindowManager; } ...... } LocalWindowManager类的成员变量mWindowManager是一个WndowManagerImpl对象,这个函数只是简单地调用WndowManagerImpl类的removeViewImmediate来进一步处理。 Step 8.WndowManagerImpl.removeViewImmediate 这个函数定义在frameworks/base/core/java/android/view/WindowManagerImpl.java文件中: publicclassWindowManagerImplimplementsWindowManager{ ...... publicvoidremoveViewImmediate(Viewview){ synchronized(this){ intindex=findViewLocked(view,true); ViewRootroot=mRoots[index]; ...... root.die(true); ...... } } ...... } 这个函数首先是找到这个view所属的ViewRoot对象root,然后调用这个root对象的die函数来销毁它。 Step 9. ViewRoot.die 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... publicvoiddie(booleanimmediate){ if(immediate){ doDie(); }else{ ...... } } ...... } 上面Step 8传进来的immediate参数为true,因此,这里直接调用doDie函数来进一步处理。 Step 10.ViewRoot.doDie 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... voiddoDie(){ ...... synchronized(this){ ...... if(mAdded){ mAdded=false; dispatchDetachedFromWindow(); } } } ...... } 当我们把Activity窗口中的View添加到一个ViewRoot对象时,就会把它的成员变量mAdded设置为true,这样就表示这个ViewRoot中有View存在,于是,这里就会调用dispatchDetachedFromWindow函数来进一步处理。 Step 11.ViewRoot.ispatchDetachedFromWindow 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... voiddispatchDetachedFromWindow(){ ...... if(mInputChannel!=null){ if(mInputQueueCallback!=null){ ...... }else{ InputQueue.unregisterInputChannel(mInputChannel); } } try{ sWindowSession.remove(mWindow); }catch(RemoteExceptione){ } ...... } ...... } 前面在介绍应用程序注册键盘消息接收通道的过程时,在Step 18,我们说到,ViewRoot类中的mInputQueueCallback为null,表示由这个ViewRoot自己来管理键盘输入事件,因此,这里首先会调用InputQueue的unregisterInputChannel函数来注销注册在应用程序这一侧的Client端InputChannel,然后再调用sWindowSession的remove函数来注销注册在InputManager这一侧的Server端InputChannel,这个逻辑是和前面介绍应用程序注册键盘消息接收通道的逻辑相对应的,前面分别注册了这两个InputChannel,现在Activity要销毁了,当然就要把它们注销了。 我们先来看注销注册在应用程序这一侧的Client端InputChannel,然后再回过头来分析注销注册在InputManager这一侧的Server端InputChannel。 Step 12.InputQueue.unregisterInputChannel 这个函数定义在frameworks/base/core/java/android/view/InputQueue.java文件中: publicfinalclassInputQueue{ ...... publicstaticvoidunregisterInputChannel(InputChannelinputChannel){ ...... synchronized(sLock){ ...... nativeUnregisterInputChannel(inputChannel); } } ...... } 这个函数只是简单地调用本地方法nativeUnregisterInputChannel来执行具体的操作。 Step 13.InputQueue.nativeUnregisterInputChannel 这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中: staticvoidandroid_view_InputQueue_nativeUnregisterInputChannel(JNIEnv*env,jclassclazz, jobjectinputChannelObj){ status_tstatus=gNativeInputQueue.unregisterInputChannel(env,inputChannelObj); ...... } 这里调用NativeInputQueue的成员函数unregisterInputChannel来进一步处理。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966657,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(17)

Step 11.InputDispatcher.dispatchOnceInnerLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::dispatchOnceInnerLocked(nsecs_tkeyRepeatTimeout, nsecs_tkeyRepeatDelay,nsecs_t*nextWakeupTime){ ...... //Readytostartanewevent. //Ifwedon'talreadyhaveapendingevent,gograbone. if(!mPendingEvent){ if(mInboundQueue.isEmpty()){ ...... }else{ //Inboundqueuehasatleastoneentry. EventEntry*entry=mInboundQueue.headSentinel.next; ...... mInboundQueue.dequeue(entry); mPendingEvent=entry; } ...... } ...... switch(mPendingEvent->type){ ...... caseEventEntry::TYPE_KEY:{ KeyEntry*typedEntry=static_cast<KeyEntry*>(mPendingEvent); ...... done=dispatchKeyLocked(currentTime,typedEntry,keyRepeatTimeout, &dropReason,nextWakeupTime); break; } ...... } ...... } 我们忽略了这个函数的次要逻辑,主要关注键盘事件的主要处理流程。首先,如果前面发生的键盘事件都已经处理完毕,那么这里的mPendingEvent就为NULL,又因为前面我们把刚刚发生的键盘事件加入了mInboundQueue队列,因此,这里mInboundQueue不为NULL,于是,这里就把mInboundQueue队列中的键盘事件取出来,放在mPendingEvent变量中: mInboundQueue.dequeue(entry); mPendingEvent=entry; 由于这里发生的是键盘事件,即mPendingEvent->type的值为EventEntry::TYPE_KEY,于是,在接下来的switch语句中就会执行dispatchKeyLocked函数来分发键盘消息。 Step 12.InputDispatcher.dispatchKeyLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: boolInputDispatcher::dispatchKeyLocked( nsecs_tcurrentTime,KeyEntry*entry,nsecs_tkeyRepeatTimeout, DropReason*dropReason,nsecs_t*nextWakeupTime){ ...... //Identifytargets. if(!mCurrentInputTargetsValid){ int32_tinjectionResult=findFocusedWindowTargetsLocked(currentTime, entry,nextWakeupTime); ...... } //Dispatchthekey. dispatchEventToCurrentInputTargetsLocked(currentTime,entry,false); returntrue; } InputDispatcher类中的mCurrentInputTargetsValid成员变量表示InputDispatcher是否已经标志出谁是当前激活的Activity窗口,如果没有,就需要通过findFocusedWindowTargetsLocked函数来把它找出来。当把当前激活的Activity窗口找出来以后,接下来就调用dispatchEventToCurrentInputTargetsLocked函数把键盘事件分发给它了。 我们先来看一InputDispatcher是如何找到当前激活的Activity窗口的,然后再分析它把键盘事件分发给当前激活Activity窗口的过程。 Step 13.InputDispatcher.findFocusedWindowTargetsLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: int32_tInputDispatcher::findFocusedWindowTargetsLocked(nsecs_tcurrentTime, constEventEntry*entry,nsecs_t*nextWakeupTime){ mCurrentInputTargets.clear(); int32_tinjectionResult; //Ifthereisnocurrentlyfocusedwindowandnofocusedapplication //thendroptheevent. if(!mFocusedWindow){ if(mFocusedApplication){ ...... injectionResult=handleTargetsNotReadyLocked(currentTime,entry, mFocusedApplication,NULL,nextWakeupTime); gotoUnresponsive; } ...... injectionResult=INPUT_EVENT_INJECTION_FAILED; gotoFailed; } //Checkpermissions. if(!checkInjectionPermission(mFocusedWindow,entry->injectionState)){ injectionResult=INPUT_EVENT_INJECTION_PERMISSION_DENIED; gotoFailed; } //Ifthecurrentlyfocusedwindowispausedthenkeepwaiting. if(mFocusedWindow->paused){ ...... injectionResult=handleTargetsNotReadyLocked(currentTime,entry, mFocusedApplication,mFocusedWindow,nextWakeupTime); gotoUnresponsive; } //Ifthecurrentlyfocusedwindowisstillworkingonpreviouseventsthenkeepwaiting. if(!isWindowFinishedWithPreviousInputLocked(mFocusedWindow)){ ...... injectionResult=handleTargetsNotReadyLocked(currentTime,entry, mFocusedApplication,mFocusedWindow,nextWakeupTime); gotoUnresponsive; } //Success!Outputtargets. injectionResult=INPUT_EVENT_INJECTION_SUCCEEDED; addWindowTargetLocked(mFocusedWindow,InputTarget::FLAG_FOREGROUND,BitSet32(0)); ...... returninjectionResult; } 回忆前面我们分析应用程序注册键盘消息接收通道的过程时,在Step 9中,当前处于激活状态的应用程序会通过调用InputDispatcher类setInputWindows函数把把当前获得焦点的Activity窗口设置到mFocusedWindow中去,因此,这里的mFocusedWindow不为NULL,于是,就通过了第一个if语句的检查。 第二个if语句检查权限问题,原来,这个键盘事件除了是由硬件触发的外,也可以由其它进程注入进来的,如果这个键盘事件是由其它进程注入进来的,那么entry->injectState就不为NULL,它里面包含了事件注册者的进程ID和用户ID,于是,这里就会调用checkInjectionPermission来检查这个事件注入者的进程ID和用户ID,看看它是否具有这个权限。这里我们不考虑这种情况,因此,这里的entry->injectState为NULL,于是,这个if语句的检查也通过了。 第三个if语句检查当前激活的Activity窗口是否是处于paused状态,如果是的话,也不用进一步处理了。一般情况下,当前激活的Activity窗口都是处于resumed状态的,于是,这个if语句的检查也通过了。 第四个if语句检查当前激活的Activity窗口是否还正在处理前一个键盘事件,如果是的话,那就要等待它处理完前一个键盘事件后再来处理新的键盘事件了。这里我们也假设当前激活的Activity窗口不是正在处理前面的键盘事件,因此,这个if语句的检查也通过了。 最后,就调用addWindowTargetLocked函数把当前激活的Activity窗口添加到InputDispatcher类的mCurrentInputTargets成员变量中去。 Step 14.InputDispatcher.addWindowTargetLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::addWindowTargetLocked(constInputWindow*window,int32_ttargetFlags, BitSet32pointerIds){ mCurrentInputTargets.push(); InputTarget&target=mCurrentInputTargets.editTop(); target.inputChannel=window->inputChannel; target.flags=targetFlags; target.xOffset=-window->frameLeft; target.yOffset=-window->frameTop; target.pointerIds=pointerIds; } 这个函数简单,就是把传进来的参数window添加到mCurrentInputTargets中去就完事了,后面InputDispatcher就会从mCurrentInputTargets中取出恰当的Activity窗口,然后把键盘事件分发给它。 回到Step 12中的dispatchKeyLocked函数,它接下来就调用dispatchEventToCurrentInputTargetsLocked来进一步处理了。 Step 15.InputDispatcher.dispatchEventToCurrentInputTargetsLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_tcurrentTime, EventEntry*eventEntry,boolresumeWithAppendedMotionSample){ ...... for(size_ti=0;i<mCurrentInputTargets.size();i++){ constInputTarget&inputTarget=mCurrentInputTargets.itemAt(i); ssize_tconnectionIndex=getConnectionIndexLocked(inputTarget.inputChannel); if(connectionIndex>=0){ sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex); prepareDispatchCycleLocked(currentTime,connection,eventEntry,&inputTarget, resumeWithAppendedMotionSample); }else{ ...... } } 这个函数的实现也比较简单,前面我们已经把当前需要接受键盘事件的Activity窗口添加到mCurrentInputTargets中去了,因此,这里就分别把它们取出来,然后调用prepareDispatchCycleLocked函数把键盘事件分发给它们处理。 前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18中(InputDispatcher.registerInputChannel),把Server端的InputChannel封装成了一个Connection,然后以这个InputChannel中的Receive Pipe Fd作为键值把这个Connection对象保存在mConnectionsByReceiveFd中。这里,既然我们已经通过mCurrentInputTargets得到了表示当前需要接收键盘事件的Activity窗口的InputTarget对象,而且这个InputTarget对象的inputChannel就表示当初在InputDispatcher中注册的Server端InputChannel,因此,这里就可以把这个Connection对象取出来,最后调用prepareDispatchCycleLocked函数来进一步处理。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966635,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(20)

Step 24. InputQueue.dispatchKeyEvent 这个函数定义在frameworks/base/core/java/android/view/InputQueue.java文件中: publicfinalclassInputQueue{ ...... privatestaticvoiddispatchKeyEvent(InputHandlerinputHandler, KeyEventevent,longfinishedToken){ RunnablefinishedCallback=FinishedCallback.obtain(finishedToken); inputHandler.handleKey(event,finishedCallback); } ...... } 这个函数首先会创建一个FinishedCallback类型的对象finishedCallback,FinishedCallback是InputQueue的一个内部类,它继承于Runnable类。这个finishedCallback对象是提供给当前Activity窗口的,当它处理完毕键盘事件后,需要通过消息分发的方式来回调这个finishedCallback对象,以及InputQueue类处理一个手尾的工作,后面我们会分析到。 这里的inputHandler对象是在前面分析应用程序注册键盘消息接收通道的过程时,在Step 1(ViewRoot.setView)中传进来的: InputQueue.registerInputChannel(mInputChannel,mInputHandler, Looper.myQueue()); 它是ViewRoot类的一个成员变量mInputHandler。因此,这里将调用ViewRoot类的内部对象mInputHandler的成员函数handleKey来处理键盘事件。 Step 25. InputHandler.handleKey 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... privatefinalInputHandlermInputHandler=newInputHandler(){ publicvoidhandleKey(KeyEventevent,RunnablefinishedCallback){ startInputEvent(finishedCallback); dispatchKey(event,true); } ...... }; ...... } 这个函数首先调用其外部类ViewRoot的startInputEvent成员函数来把回调对象finishedCallback保存下来: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... privatevoidstartInputEvent(RunnablefinishedCallback){ ...... mFinishedCallback=finishedCallback; } ...... } 然后再调用其外部类ViewRoot的dispatchKey成员函数来进一步处这个键盘事件。 Step 26.ViewRoot.dispatchKey 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... privatevoiddispatchKey(KeyEventevent,booleansendDone){ ...... Messagemsg=obtainMessage(DISPATCH_KEY); msg.obj=event; msg.arg1=sendDone?1:0; ...... sendMessageAtTime(msg,event.getEventTime()); } ...... } ViewRoot不是直接处理这个键盘事件,而是把作为一个消息(DISPATCH_KEY)它放到消息队列中去处理,这个消息最后由ViewRoot类的deliverKeyEvent成员函数来处理。 Step 27.ViewRoot.deliverKeyEvent 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... privatevoiddeliverKeyEvent(KeyEventevent,booleansendDone){ //IfmViewisnull,wejustconsumethekeyeventbecauseitdoesn't //makesensetodoanythingelsewithit. booleanhandled=mView!=null ?mView.dispatchKeyEventPreIme(event):true; ...... //Ifitispossibleforthiswindowtointeractwiththeinput //methodwindow,thenwewanttofirstdispatchourkeyevents //totheinputmethod. if(mLastWasImTarget){ InputMethodManagerimm=InputMethodManager.peekInstance(); if(imm!=null&&mView!=null){ ...... imm.dispatchKeyEvent(mView.getContext(),seq,event, mInputMethodCallback); return; } } ...... } ...... } ViewRoot在把这个键盘事件分发给当前激活的Activity窗口处理之前,首先会调用InputMethodManager的dispatchKeyEvent成员函数来处理这个键盘事件。InputMethodManager处理完这个键盘事件后,再回调用这里的mInputMethodCallback对象的finishedEvent成员函数来把键盘事件分发给当前激活的Activity窗口处理。当然,在把这个键盘事件分发给InputMethodManager处理之前,ViewRoot也会先把这个键盘事件分发给当前激活的Activity窗口的dispatchKeyEventPreIme成员函数处理。 Step 28. InputMethodManager.dispatchKeyEvent 这个函数定义在frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java文件中。这是一个输入法相关的类,我们这里就不关注了,只要知道当输入法处理完成之后,它就会调用ViewRoot类的mInputMehtodCallback对象的finishedEvent成员函数。 Step 29. InputMethodCallack.finishedEvent 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... staticclassInputMethodCallbackextendsIInputMethodCallback.Stub{ privateWeakReference<ViewRoot>mViewRoot; publicInputMethodCallback(ViewRootviewRoot){ mViewRoot=newWeakReference<ViewRoot>(viewRoot); } publicvoidfinishedEvent(intseq,booleanhandled){ finalViewRootviewRoot=mViewRoot.get(); if(viewRoot!=null){ viewRoot.dispatchFinishedEvent(seq,handled); } } ...... } ...... } 这个函数最终调用ViewRoot的dispatchFinishedEvent来进一步处理。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966639,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(8)

回到Step 18中,我们继续分析EventHub.getEvent函数的实现。 在中间的for循环里面,首先会检查当前是否有输入设备被关闭,如果有,就返回一个设备移除的事件给调用方: //Reportanydevicesthathadlastbeenadded/removed. if(mClosingDevices!=NULL){ device_t*device=mClosingDevices; LOGV("Reportingdeviceclosed:id=0x%x,name=%s\n", device->id,device->path.string()); mClosingDevices=device->next; if(device->id==mFirstKeyboardId){ outEvent->deviceId=0; }else{ outEvent->deviceId=device->id; } outEvent->type=DEVICE_REMOVED; outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC); deletedevice; mNeedToSendFinishedDeviceScan=true; returntrue; } 接着,检查当前是否有新的输入设备加入进来: if(mOpeningDevices!=NULL){ device_t*device=mOpeningDevices; LOGV("Reportingdeviceopened:id=0x%x,name=%s\n", device->id,device->path.string()); mOpeningDevices=device->next; if(device->id==mFirstKeyboardId){ outEvent->deviceId=0; }else{ outEvent->deviceId=device->id; } outEvent->type=DEVICE_ADDED; outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC); mNeedToSendFinishedDeviceScan=true; returntrue; } 接着,再检查是否需要结束监控输入事件: if(mNeedToSendFinishedDeviceScan){ mNeedToSendFinishedDeviceScan=false; outEvent->type=FINISHED_DEVICE_SCAN; outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC); returntrue; } 最后,就是要检查当前是否有还未处理的输入设备事件发生了: //Grabthenextinputevent. for(;;){ //Consumebufferedinputevents,ifany. if(mInputBufferIndex<mInputBufferCount){ conststructinput_event&iev=mInputBufferData[mInputBufferIndex++]; constdevice_t*device=mDevices[mInputDeviceIndex]; LOGV("%sgot:t0=%d,t1=%d,type=%d,code=%d,v=%d",device->path.string(), (int)iev.time.tv_sec,(int)iev.time.tv_usec,iev.type,iev.code,iev.value); if(device->id==mFirstKeyboardId){ outEvent->deviceId=0; }else{ outEvent->deviceId=device->id; } outEvent->type=iev.type; outEvent->scanCode=iev.code; if(iev.type==EV_KEY){ status_terr=device->layoutMap->map(iev.code, &outEvent->keyCode,&outEvent->flags); LOGV("iev.code=%dkeyCode=%dflags=0x%08xerr=%d\n", iev.code,outEvent->keyCode,outEvent->flags,err); if(err!=0){ outEvent->keyCode=AKEYCODE_UNKNOWN; outEvent->flags=0; } }else{ outEvent->keyCode=iev.code; } outEvent->value=iev.value; //Useaneventtimestampinthesametimebaseas //java.lang.System.nanoTime()andandroid.os.SystemClock.uptimeMillis() //asexpectedbytherestofthesystem. outEvent->when=systemTime(SYSTEM_TIME_MONOTONIC); returntrue; } //Finishreadingalleventsfromdevicesidentifiedinpreviouspoll(). //ThiscodeassumesthatmInputDeviceIndexisinitially0andthatthe //reventsmemberofpollfdisinitializedto0whenthedeviceisfirstadded. //SincemFDs[0]isusedforinotify,weprocessregulareventsstartingatindex1. mInputDeviceIndex+=1; if(mInputDeviceIndex>=mFDCount){ break; } conststructpollfd&pfd=mFDs[mInputDeviceIndex]; if(pfd.revents&POLLIN){ int32_treadSize=read(pfd.fd,mInputBufferData, sizeof(structinput_event)*INPUT_BUFFER_SIZE); if(readSize<0){ if(errno!=EAGAIN&&errno!=EINTR){ LOGW("couldnotgetevent(errno=%d)",errno); } }elseif((readSize%sizeof(structinput_event))!=0){ LOGE("couldnotgetevent(wrongsize:%d)",readSize); }else{ mInputBufferCount=readSize/sizeof(structinput_event); mInputBufferIndex=0; } } } 未处理的输入事件保存在成员变量mInputBufferData中,如果有的话,就可以直接返回了,否则的话,就要通过系统调用poll来等待输入设备上发生新的事件了,在我们这个场景中,就是等待键盘有键被按下或者松开了。: intpollResult=poll(mFDs,mFDCount,-1); 这里的mFDs包含了我们所要监控的输入设备的打开文件描述符,这是在前面的openPlatformInput函数中初始化的。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966621,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(6)

Step 21.EventHub.openDevice这个函数定义在frameworks/base/libs/ui/EventHub.cpp文件中: intEventHub::openDevice(constchar*deviceName){ intversion; intfd; structpollfd*new_mFDs; device_t**new_devices; char**new_device_names; charname[80]; charlocation[80]; charidstr[80]; structinput_idid; LOGV("Openingdevice:%s",deviceName); AutoMutex_l(mLock); fd=open(deviceName,O_RDWR); if(fd<0){ LOGE("couldnotopen%s,%s\n",deviceName,strerror(errno)); return-1; } ...... intdevid=0; while(devid<mNumDevicesById){ if(mDevicesById[devid].device==NULL){ break; } devid++; } ...... mDevicesById[devid].seq=(mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK; if(mDevicesById[devid].seq==0){ mDevicesById[devid].seq=1<<SEQ_SHIFT; } new_mFDs=(pollfd*)realloc(mFDs,sizeof(mFDs[0])*(mFDCount+1)); new_devices=(device_t**)realloc(mDevices,sizeof(mDevices[0])*(mFDCount+1)); if(new_mFDs==NULL||new_devices==NULL){ LOGE("outofmemory"); return-1; } mFDs=new_mFDs; mDevices=new_devices; ...... device_t*device=newdevice_t(devid|mDevicesById[devid].seq,deviceName,name); if(device==NULL){ LOGE("outofmemory"); return-1; } device->fd=fd; mFDs[mFDCount].fd=fd; mFDs[mFDCount].events=POLLIN; mFDs[mFDCount].revents=0; //Figureoutthekindsofeventsthedevicereports. uint8_tkey_bitmask[sizeof_bit_array(KEY_MAX+1)]; memset(key_bitmask,0,sizeof(key_bitmask)); LOGV("Gettingkeys..."); if(ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(key_bitmask)),key_bitmask)>=0){ //Seeifthisisakeyboard.Ignoreeverythinginthebuttonrangeexceptfor //gamepadswhicharealsoconsideredkeyboards. if(containsNonZeroByte(key_bitmask,0,sizeof_bit_array(BTN_MISC)) ||containsNonZeroByte(key_bitmask,sizeof_bit_array(BTN_GAMEPAD), sizeof_bit_array(BTN_DIGI)) ||containsNonZeroByte(key_bitmask,sizeof_bit_array(KEY_OK), sizeof_bit_array(KEY_MAX+1))){ device->classes|=INPUT_DEVICE_CLASS_KEYBOARD; device->keyBitmask=newuint8_t[sizeof(key_bitmask)]; if(device->keyBitmask!=NULL){ memcpy(device->keyBitmask,key_bitmask,sizeof(key_bitmask)); }else{ deletedevice; LOGE("outofmemoryallocatingkeybitmask"); return-1; } } } ...... if((device->classes&INPUT_DEVICE_CLASS_KEYBOARD)!=0){ chartmpfn[sizeof(name)]; charkeylayoutFilename[300]; //amoredescriptivename device->name=name; //replaceallthespaceswithunderscores strcpy(tmpfn,name); for(char*p=strchr(tmpfn,'');p&&*p;p=strchr(tmpfn,'')) *p='_'; //findthe.klfileweneedforthisdevice constchar*root=getenv("ANDROID_ROOT"); snprintf(keylayoutFilename,sizeof(keylayoutFilename), "%s/usr/keylayout/%s.kl",root,tmpfn); booldefaultKeymap=false; if(access(keylayoutFilename,R_OK)){ snprintf(keylayoutFilename,sizeof(keylayoutFilename), "%s/usr/keylayout/%s",root,"qwerty.kl"); defaultKeymap=true; } status_tstatus=device->layoutMap->load(keylayoutFilename); if(status){ LOGE("Error%dloadingkeylayout.",status); } //telltheworldaboutthedevname(thedescriptivename) if(!mHaveFirstKeyboard&&!defaultKeymap&&strstr(name,"-keypad")){ //thebuilt-inkeyboardhasawell-knowndeviceIDof0, //thisdevicebetternotgoaway. mHaveFirstKeyboard=true; mFirstKeyboardId=device->id; property_set("hw.keyboards.0.devname",name); }else{ //ensuremFirstKeyboardIdissetto-something-. if(mFirstKeyboardId==0){ mFirstKeyboardId=device->id; } } charpropName[100]; sprintf(propName,"hw.keyboards.%u.devname",device->id); property_set(propName,name); //'Q'keysupport=cheaptestofwhetherthisisanalpha-capablekbd if(hasKeycodeLocked(device,AKEYCODE_Q)){ device->classes|=INPUT_DEVICE_CLASS_ALPHAKEY; } //SeeifthisdevicehasaDPAD. if(hasKeycodeLocked(device,AKEYCODE_DPAD_UP)&& hasKeycodeLocked(device,AKEYCODE_DPAD_DOWN)&& hasKeycodeLocked(device,AKEYCODE_DPAD_LEFT)&& hasKeycodeLocked(device,AKEYCODE_DPAD_RIGHT)&& hasKeycodeLocked(device,AKEYCODE_DPAD_CENTER)){ device->classes|=INPUT_DEVICE_CLASS_DPAD; } //Seeifthisdevicehasagamepad. for(size_ti=0;i<sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]);i++){ if(hasKeycodeLocked(device,GAMEPAD_KEYCODES[i])){ device->classes|=INPUT_DEVICE_CLASS_GAMEPAD; break; } } LOGI("Newkeyboard:device->id=0x%xdevname='%s'propName='%s'keylayout='%s'\n", device->id,name,propName,keylayoutFilename); } ...... mDevicesById[devid].device=device; device->next=mOpeningDevices; mOpeningDevices=device; mDevices[mFDCount]=device; mFDCount++; return0; } 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966617,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序消息处理机制(Looper、Handler)分析(5)

ActivityThread类的这个mH成员变量是什么时候创建的呢?我们前面在分析应用程序的消息循环时,说到当应用程序进程启动之后,就会加载ActivityThread类的main函数里面,在这个main函数里面,在通过Looper类进入消息循环之前,会在当前进程中创建一个ActivityThread实例: publicfinalclassActivityThread{ ...... publicstaticfinalvoidmain(String[]args){ ...... ActivityThreadthread=newActivityThread(); thread.attach(false); ...... } } 在创建这个实例的时候,就会同时创建其成员变量mH了: publicfinalclassActivityThread{ ...... finalHmH=newH(); ...... } 前面说过,H类继承于Handler类,因此,当创建这个H对象时,会调用Handler类的构造函数,这个函数定义在frameworks/base/core/java/android/os/Handler.java文件中: publicclassHandler{ ...... publicHandler(){ ...... mLooper=Looper.myLooper(); ...... mQueue=mLooper.mQueue; ...... } finalMessageQueuemQueue; finalLoopermLooper; ...... } 在Hanlder类的构造函数中,主要就是初始成员变量mLooper和mQueue了。这里的myLooper是Looper类的静态成员函数,通过它来获得一个Looper对象,这个Looper对象就是前面我们在分析消息循环时,在ActivityThread类的main函数中通过Looper.prepareMainLooper函数创建的。Looper.myLooper函数实现在frameworks/base/core/java/android/os/Looper.java文件中: publicclassLooper{ ...... publicstaticfinalLoopermyLooper(){ return(Looper)sThreadLocal.get(); } ...... } 有了这个Looper对象后,就可以通过Looper.mQueue来访问应用程序的消息队列了。 有了这个Handler对象mH后,就可以通过它来往应用程序的消息队列中加入新的消息了。回到前面的queueOrSendMessage函数中,当它准备好了一个Message对象msg后,就开始调用mH.sendMessage函数来发送消息了,这个函数定义在frameworks/base/core/java/android/os/Handler.java文件中: 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; } ...... } 在发送消息时,是可以指定消息的处理时间的,但是通过sendMessage函数发送的消息的处理时间默认就为当前时间,即表示要马上处理,因此,从sendMessage函数中调用sendMessageDelayed函数,传入的时间参数为0,表示这个消息不要延时处理,而在sendMessageDelayed函数中,则会先获得当前时间,然后加上消息要延时处理的时间,即得到这个处理这个消息的绝对时间,然后调用sendMessageAtTime函数来把消息加入到应用程序的消息队列中去。 在sendMessageAtTime函数,首先得到应用程序的消息队列mQueue,这是在Handler对象构造时初始化好的,前面已经分析过了,接着设置这个消息的目标对象target,即这个消息最终是由谁来处理的: msg.target=this; 这里将它赋值为this,即表示这个消息最终由这个Handler对象来处理,即由ActivityThread对象的mH成员变量来处理。 函数最后调用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; } ...... } 把消息加入到消息队列时,分两种情况,一种当前消息队列为空时,这时候应用程序的主线程一般就是处于空闲等待状态了,这时候就要唤醒它,另一种情况是应用程序的消息队列不为空,这时候就不需要唤醒应用程序的主线程了,因为这时候它一定是在忙着处于消息队列中的消息,因此不会处于空闲等待的状态。 第一种情况比较简单,只要把消息放在消息队列头就可以了: msg.next=p; mMessages=msg; needWake=mBlocked;//newhead,mightneedtowakeup 第二种情况相对就比较复杂一些了,前面我们说过,当往消息队列中发送消息时,是可以指定消息的处理时间的,而消息队列中的消息,就是按照这个时间从小到大来排序的,因此,当把新的消息加入到消息队列时,就要根据它的处理时间来找到合适的位置,然后再放进消息队列中去: Messageprev=null; while(p!=null&&p.when<=when){ prev=p; p=p.next; } msg.next=prev.next; prev.next=msg; needWake=false;//stillwaitingonhead,noneedtowakeup 把消息加入到消息队列去后,如果应用程序的主线程正处于空闲等待状态,就需要调用natvieWake函数来唤醒它了,这是一个JNI方法,定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中: staticvoidandroid_os_MessageQueue_nativeWake(JNIEnv*env,jobjectobj,jintptr){ NativeMessageQueue*nativeMessageQueue=reinterpret_cast<NativeMessageQueue*>(ptr); returnnativeMessageQueue->wake(); } 这个JNI层的NativeMessageQueue对象我们在前面分析消息循环的时候创建好的,保存在Java层的MessageQueue对象的mPtr成员变量中,这里把它取回来之后,就调用它的wake函数来唤醒应用程序的主线程,这个函数也是定义在frameworks/base/core/jni/android_os_MessageQueue.cpp文件中: voidNativeMessageQueue::wake(){ mLooper->wake(); } 这里它又通过成员变量mLooper的wake函数来执行操作,这里的mLooper成员变量是一个C++层实现的Looper对象,它定义在frameworks/base/libs/utils/Looper.cpp文件中: voidLooper::wake(){ ...... ssize_tnWrite; do{ nWrite=write(mWakeWritePipeFd,"W",1); }while(nWrite==-1&&errno==EINTR); ....... } 这个wake函数很简单,只是通过打开文件描述符mWakeWritePipeFd往管道的写入一个"W"字符串。其实,往管道写入什么内容并不重要,往管道写入内容的目的是为了唤醒应用程序的主线程。前面我们在分析应用程序的消息循环时说到,当应用程序的消息队列中没有消息处理时,应用程序的主线程就会进入空闲等待状态,而这个空闲等待状态就是通过调用这个Looper类的pollInner函数来进入的,具体就是在pollInner函数中调用epoll_wait函数来等待管道中有内容可读的。 这时候既然管道中有内容可读了,应用程序的主线程就会从这里的Looper类的pollInner函数返回到JNI层的nativePollOnce函数,最后返回到Java层中的MessageQueue.next函数中去,这里它就会发现消息队列中有新的消息需要处理了,于就会处理这个消息。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966601,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序消息处理机制(Looper、Handler)分析(4)

2. 消息的发送 应用程序的主线程准备就好消息队列并且进入到消息循环后,其它地方就可以往这个消息队列中发送消息了。我们继续以文章开始介绍的Android应用程序启动过程源代码分析一文中的应用程序启动过为例,说明应用程序是如何把消息加入到应用程序的消息队列中去的。 在Android应用程序启动过程源代码分析这篇文章的Step 30中,ActivityManagerService通过调用ApplicationThread类的scheduleLaunchActivity函数通知应用程序,它可以加载应用程序的默认Activity了,这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中: 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); } ...... } ...... } 这里把相关的参数都封装成一个ActivityClientRecord对象r,然后调用queueOrSendMessage函数来往应用程序的消息队列中加入一个新的消息(H.LAUNCH_ACTIVITY),这个函数定义在frameworks/base/core/java/android/app/ActivityThread.java文件中: 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); } } ...... } ...... } 在queueOrSendMessage函数中,又进一步把上面传进来的参数封装成一个Message对象msg,然后通过mH.sendMessage函数把这个消息对象msg加入到应用程序的消息队列中去。这里的mH是ActivityThread类的成员变量,它的类型为H,继承于Handler类,它定义在frameworks/base/core/java/android/app/ActivityThread.java文件中: publicfinalclassActivityThread{ ...... privatefinalclassHextendsHandler{ ...... publicvoidhandleMessage(Messagemsg){ ...... switch(msg.what){ ...... } ...... } ...... } 这个H类就是通过其成员函数handleMessage函数来处理消息的了,后面我们分析消息的处理过程时会看到。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966600,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android应用程序键盘(Keyboard)消息处理机制分析(18)

Step 16.InputDispatcher.prepareDispatchCycleLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::prepareDispatchCycleLocked(nsecs_tcurrentTime, constsp<Connection>&connection,EventEntry*eventEntry,constInputTarget*inputTarget, boolresumeWithAppendedMotionSample){ ...... //Resumethedispatchcyclewithafreshlyappendedmotionsample. //Firstwecheckthatthelastdispatchentryintheoutboundqueueisforthesame //motioneventtowhichweappendedthemotionsample.Ifwefindsuchadispatch //entry,andifitiscurrentlyinprogressthenwetrytostreamthenewsample. boolwasEmpty=connection->outboundQueue.isEmpty(); if(!wasEmpty&&resumeWithAppendedMotionSample){ ...... return; } //Thisisanewevent. //Enqueueanewdispatchentryontotheoutboundqueueforthisconnection. DispatchEntry*dispatchEntry=mAllocator.obtainDispatchEntry(eventEntry,//incrementsref inputTarget->flags,inputTarget->xOffset,inputTarget->yOffset); ...... //Enqueuethedispatchentry. connection->outboundQueue.enqueueAtTail(dispatchEntry); //Iftheoutboundqueuewaspreviouslyempty,startthedispatchcyclegoing. if(wasEmpty){ ...... startDispatchCycleLocked(currentTime,connection); } } 在开始处理键盘事件之前,这个函数会检查一下传进来的参数connection中的outboundQueue事件队列是否为空,如果不为空,就要看看当前要处理的事件和outboundQueue队列中的最后一个事件是不是同一个motion事件,如果是的话,并且从上面传进来的resumeWithAppendedMotionSample参数为true,这时候就要以流水线的方式来处理这些motion事件了。在我们这个情景中,要处理的是键盘事件,因此在上面Step 12中传进来的resumeWithAppendedMotionSample参数为false,因此,我们略过这种情况。 接下来,就会把当前的键盘事件封装成一个DispatchEntry对象,然后添加到connection对象的outboundQueue队列中去,表示当前键盘事件是一个待处理的键盘事件。 当connection中的outboundQueue事件队列不为空,即wasEmpty为false时,说明当前这个Activity窗口正在处键盘事件了,因此,就不需要调用startDispatchCycleLocked来启动Activity窗口来处理这个事件了,因为一旦这个Activity窗口正在处键盘事件,它就会一直处理下去,直到它里的connection对象的outboundQueue为空为止。当connection中的outboundQueue事件队列为空时,就需要调用startDispatchCycleLocked来通知这个Activity窗口来执行键盘事件处理的流程了。 Step 17.InputDispatcher.startDispatchCycleLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::startDispatchCycleLocked(nsecs_tcurrentTime, constsp<Connection>&connection){ ...... DispatchEntry*dispatchEntry=connection->outboundQueue.headSentinel.next; //Markthedispatchentryasinprogress. dispatchEntry->inProgress=true; //Updatetheconnection'sinputstate. EventEntry*eventEntry=dispatchEntry->eventEntry; ...... //Publishtheevent. status_tstatus; switch(eventEntry->type){ caseEventEntry::TYPE_KEY:{ KeyEntry*keyEntry=static_cast<KeyEntry*>(eventEntry); //Applytargetflags. int32_taction=keyEntry->action; int32_tflags=keyEntry->flags; //Publishthekeyevent. status=connection->inputPublisher.publishKeyEvent(keyEntry->deviceId,keyEntry->source, action,flags,keyEntry->keyCode,keyEntry->scanCode, keyEntry->metaState,keyEntry->repeatCount,keyEntry->downTime, keyEntry->eventTime); ...... break; } ...... } //Sendthedispatchsignal. status=connection->inputPublisher.sendDispatchSignal(); ...... } 这个函数主要围绕传进来的Connection对象做两件事情,一是从它的outboundQueue队列中取出当前需要处理的键盘事件,然后把这个事件记录在它的内部对象inputPublisher中,二是通过它的内部对象inputPublisher通知它所关联的Activity窗口,现在有键盘事件需要处理了。第一件事情是通过调用它的InputPublisher对象的publishKeyEvent函数来完成的,而第二件事情是通过调用它的InputPublisher对象的sendDispatchSignal来完成的。我们先来看InputPublisher的成员函数publishKeyEvent的实现,然后再回来分析它的另外一个成员函数sendDispatchSignal的实现。 Step 18. InputPublisher.publishKeyEvent 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::publishKeyEvent( int32_tdeviceId, int32_tsource, int32_taction, int32_tflags, int32_tkeyCode, int32_tscanCode, int32_tmetaState, int32_trepeatCount, nsecs_tdownTime, nsecs_teventTime){ ...... status_tresult=publishInputEvent(AINPUT_EVENT_TYPE_KEY,deviceId,source); if(result<0){ returnresult; } mSharedMessage->key.action=action; mSharedMessage->key.flags=flags; mSharedMessage->key.keyCode=keyCode; mSharedMessage->key.scanCode=scanCode; mSharedMessage->key.metaState=metaState; mSharedMessage->key.repeatCount=repeatCount; mSharedMessage->key.downTime=downTime; mSharedMessage->key.eventTime=eventTime; returnOK; } 这个函数主要就是把键盘事件记录在InputPublisher类的成员变量mSharedMessage中了,这个mSharedMessage成员变量指向的是一个匿名共享内存。 这个匿名共享内存是什么时候创建的呢?前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18中(InputDispatcher.registerInputChannel),在把Server端的InputChannel封装成一个 Connection对象时,会调用它的initialize成员函数来执行一些初始化工作,就是在这个时候创建这个匿名共享内存的了: sp<Connection>connection=newConnection(inputChannel); status_tstatus=connection->initialize(); 我们来看一下这个initialize函数的实现,它定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::initialize(){ ...... intashmemFd=mChannel->getAshmemFd(); intresult=ashmem_get_size_region(ashmemFd); ...... mAshmemSize=(size_t)result; mSharedMessage=static_cast<InputMessage*>(mmap(NULL,mAshmemSize, PROT_READ|PROT_WRITE,MAP_SHARED,ashmemFd,0)); ...... mPinned=true; mSharedMessage->consumed=false; returnreset(); } InputPublisher的成员变量mChannel就是指注册在InputDispatcher中的Server端InputChannel了。我们知道,这个InputChannel除了拥有一个反向管道的读端文件描述符和一个前向管道的写端文件描述符之后,还有一个匿名共享文件描述符,这个匿名共享文件描述符就是用来创建匿名共享内存mSharedMessage的了。 这个匿名共享内存mSharedMessage的作用是什么呢?原来,在InputChannel中,前向管道和反向管道的作用只是用来在Server端和Client端之间相互通知有事件发生了,但是具体是什么样的事件,还需要去读取这个匿名共享内存的内容才知道。前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 14中(InputChannel.openInputChannelPair)创建Server端和Client端的InputChannel对时,创建一个匿名共享内存,这个匿名共享内存有两个文件描述符同时指向它,其中一个放在Server端的InputChannel中,另外一个放在Client端的InputChannel中。这样,当InputDispatcher通过Server端的InputChannel的前向管道来通知Client端有键盘事件发生时,Client端只要通过它的InputChannel中的匿名共享内存文件描述符去读取匿名共享内存中的内容,就可以知道发生了什么事情了。 回到Step 17中,接下来就是调用InputPublisher的成员函数sendDispatchSignal来通知Activity窗口处理键盘事件了。 Step 19.InputPublishe.sendDispatchSignal 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::sendDispatchSignal(){ ...... mWasDispatched=true; returnmChannel->sendSignal(INPUT_SIGNAL_DISPATCH); } 这个函数很简单,它通过调用内部成员变量mChannel的sendSignal函数来通知相应的Activity窗口来处理键盘事件。 Step 20. InputChannel.sendSignal 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputChannel::sendSignal(charsignal){ ssize_tnWrite; do{ nWrite=::write(mSendPipeFd,&signal,1); }while(nWrite==-1&&errno==EINTR); if(nWrite==1){ ...... returnOK; } return-errno; } 这里所谓的发送信号通知,其实是通过向其内部一个管道的写端写入一个字符来实现的。前面我们分析应用程序注册键盘消息接收通道的过程时,在Step 21中(NativeInputQueue.registerInputChannel),它把一个InputChannel注册到应用程序主线程中的Looper对象中,然后应用程序的主线程就通过这个Looper对象睡眠等待在这个InputChannel中的前向管道中有新的内容可读了,这里的mSendPipeFd就是对应这个前向管道的写端。现在既然向这个前向管道的写端写入新的内容了,于是,应用程序的主线程就被唤醒了。 在前面分析应用程序注册键盘消息接收通道过程的Step 21中,我们也说过,当应用程序的主线程因为这个InputChannel中的前向管道的写端唤醒时,NativeInputQueue的成员函数handleReceiveCallback就会被回调,因此,接下来,应用程序的主线程就会被唤醒,然后执行NativeInputQueue的成员函数handleReceiveCallback。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966636,如需转载请自行联系原作者

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册