首页 文章 精选 留言 我的

精选列表

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

利用java反射机制,使用android系统的内部类成员函数

//访问某个属性 Context mmsCtx = createPackageContext( "com.android.lockscreen" , Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); Class<?> maClass = Class.forName( "com.android.lockscreen.LockScreenActivity" , true , mmsCtx.getClassLoader()); Object maObject = maClass.newInstance(); Field f_mPm = maClass.getDeclaredField( "TAG" ); f_mPm.setAccessible( true ); Log.i(LOG_TAG, "___________________________________________obj:" +f_mPm.get(maObject)); //访问方法 //访问 try { Context mmsCtx = createPackageContext( "com.android.lockscreen" , Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); Class<?> maClass = Class.forName( "com.android.lockscreen.LockScreenActivity" , true , mmsCtx.getClassLoader()); Object maObject = maClass.newInstance(); Field f_mPm = maClass.getDeclaredField( "TAG" ); //maClass.getDeclaredMethod("", parameterTypes); f_mPm.setAccessible( true ); Log.i(LOG_TAG, "___________________________________________obj:" +f_mPm.get(maObject)); Class[] resource = new Class[]{Context. class }; Object[] resourceObj = new Object[]{mmsCtx}; Method addsMethod = maClass.getDeclaredMethod( "getLockScreenView" , resource); addsMethod.setAccessible( true ); View addsResult = (View)addsMethod.invoke(maObject, resourceObj); Log.i(LOG_TAG, "___________________________________________addsResult:" +addsResult); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } //定义被访问 public class LockScreenActivity extends Activity { private String TAG = "LockScreenActivity" ; //private LockScreen mScreen = new LockScreen(LockScreenActivity.this); private LockScreen getLockScreenView(Context context){ Log.i(TAG, "_________________________________________context:" +context); return new LockScreen(context); } } //获取setting包的的Context Context mmsCtx = createPackageContext( "com.android.settings" , Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); //使用setting的classloader加载com.android.settings.ManageApplications类 Class<?> maClass = Class.forName( "com.android.settings.ManageApplications" , true , mmsCtx.getClassLoader()); //创建它的一个对象 Object maObject = maClass.newInstance(); /* * 将私有域mPm赋值。因为mPm在SizeObserver的invokeGetSize中用到了, * 却因为没有执行onCreate而没有初始化,所以要在此处初始化。 */ Field f_mPm = maClass.getDeclaredField( "mPm" ); f_mPm.setAccessible( true ); f_mPm.set(maObject, mmsCtx.getPackageManager()); 分类: android solve 本文转自wanqi博客园博客,原文链接http://www.cnblogs.com/wanqieddy/archive/2011/10/25/2224200.html :如需转载请自行联系原作者

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

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

Step 21.NativeInputQueue.handleReceiveCallback 这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中: intNativeInputQueue::handleReceiveCallback(intreceiveFd,intevents,void*data){ NativeInputQueue*q=static_cast<NativeInputQueue*>(data); JNIEnv*env=AndroidRuntime::getJNIEnv(); sp<Connection>connection; InputEvent*inputEvent; jobjectinputHandlerObjLocal; jlongfinishedToken; {//acquirelock AutoMutex_l(q->mLock); ssize_tconnectionIndex=q->mConnectionsByReceiveFd.indexOfKey(receiveFd); ...... connection=q->mConnectionsByReceiveFd.valueAt(connectionIndex); ...... status_tstatus=connection->inputConsumer.receiveDispatchSignal(); if(status){ ...... return0;//removethecallback } ...... status=connection->inputConsumer.consume(&connection->inputEventFactory,&inputEvent); ...... finishedToken=generateFinishedToken(receiveFd,connection->id,connection->messageSeqNum); inputHandlerObjLocal=env->NewLocalRef(connection->inputHandlerObjGlobal); }//releaselock ...... int32_tinputEventType=inputEvent->getType(); jobjectinputEventObj; jmethodIDdispatchMethodId; switch(inputEventType){ caseAINPUT_EVENT_TYPE_KEY: ...... inputEventObj=android_view_KeyEvent_fromNative(env, static_cast<KeyEvent*>(inputEvent)); dispatchMethodId=gInputQueueClassInfo.dispatchKeyEvent; break; } ...... } ...... env->CallStaticVoidMethod(gInputQueueClassInfo.clazz, dispatchMethodId,inputHandlerObjLocal,inputEventObj, jlong(finishedToken)); ...... return1; } 这个函数首先是通过参数data获得当初注册InputChannel的NativeInputQueue对象,具体可以参考前面介绍的应用程序注册键盘消息接收通道过程的Step 21。接下来再通过参数receiveFd获得保存在这个NativeInputQueue对象中的mConnectionsByReceiveFd成员变量中的Connection对象。有了这个Connection对象后,就可以获得它内部的InputConsumer对象,这个InputConsumer对象是和上面的Step 18中介绍的InputPublisher对象相应的。 在InputChannel内部中,分别有一个InputPublisher对象和一个InputConsumer对象,对于Server端的InputChannel来说,它使用的是InputPublisher对象,通过它进行键盘消息的分发,而对于Client端的InputChannel来说,它使用的是InputConsumer对象,通过它进行键盘消息的读取。 获得了这个InputConsumer对象后首先是调用它的receiveDispatchSignal来确认是否是接收到了键盘消息的通知,如果是的话,再调用它的consume函数来把键盘事件读取出来,最后,调用Java层的回调对象InputQueue的DispatchKeyEvent来处理这个键盘事件。下面,我们就依次来分析这些过程。 Step 22.InputConsumer.receiveDispatchSignal 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputConsumer::receiveDispatchSignal(){ ...... charsignal; status_tresult=mChannel->receiveSignal(&signal); if(result){ returnresult; } if(signal!=INPUT_SIGNAL_DISPATCH){ ...... returnUNKNOWN_ERROR; } returnOK; } 这个函数很简单,它通过它内部对象mChannel来从前向管道的读端读入一个字符,看看是否是前面的Step 20中写入的INPUT_SIGNAL_DISPATCH字符。 InputChannel类的receiveSignal函数也是定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputChannel::receiveSignal(char*outSignal){ ssize_tnRead; do{ nRead=::read(mReceivePipeFd,outSignal,1); }while(nRead==-1&&errno==EINTR); if(nRead==1){ ...... returnOK; } ...... return-errno; } Step 23.InputConsumer.consume 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputConsumer::consume(InputEventFactoryInterface*factory,InputEvent**outEvent){ ...... *outEvent=NULL; intashmemFd=mChannel->getAshmemFd(); intresult=ashmem_pin_region(ashmemFd,0,0); ...... if(mSharedMessage->consumed){ ...... returnINVALID_OPERATION; } //Acquirebut*neverrelease*thesemaphore.Contentiononthesemaphoreisusedtosignal //tothepublisherthatthemessagehasbeenconsumed(orisintheprocessofbeing //consumed).Eventuallythepublisherwillreinitializethesemaphoreforthenextmessage. result=sem_wait(&mSharedMessage->semaphore); ...... mSharedMessage->consumed=true; switch(mSharedMessage->type){ caseAINPUT_EVENT_TYPE_KEY:{ KeyEvent*keyEvent=factory->createKeyEvent(); if(!keyEvent)returnNO_MEMORY; populateKeyEvent(keyEvent); *outEvent=keyEvent; break; } ...... } returnOK; } 这个函数很简单,只要对照前面的Step 18(InputPublisher.publishKeyEvent)来逻辑来看就可以了,后者是往匿名共享内存中写入键盘事件,前者是从这个匿名共享内存中把这个键盘事件的内容读取出来。 回到Step 21中的handleReceiveCallback函数中,从InputConsumer中获得了键盘事件的内容(保存在本地变量inputEvent中)后,就开始要通知Java层的应用程序了。在前面分析应用程序注册键盘消息接收通道的过程时,在Step 21中(NativeInputQueue.registerInputChannel),会把传进来的对象inputHandlerObj保存在Connection对象中: connection->inputHandlerObjGlobal=env->NewGlobalRef(inputHandlerObj); 这个inputHandlerObj对象的类型为Java层的InputHandler对象,因此,这里首先把它取回来: inputHandlerObjLocal=env->NewLocalRef(connection->inputHandlerObjGlobal); 取回来之后,我们要把作为参数来调用InputQueue类的dispatchKeyEvent静态成员函数来通知应用程序,有键盘事件发生了,因此,先找到InputQueue类的静态成员函数dispatchKeyEvent的ID: dispatchMethodId=gInputQueueClassInfo.dispatchKeyEvent; 在回调用这个InputQueue类的dispatchKeyEvent静态成员函数之前,还要把前面获得的inputEvent对象转换成Java层的KeyEvent对象: inputEventObj=android_view_KeyEvent_fromNative(env, static_cast<KeyEvent*>(inputEvent)); 万事具备了,就可以通知Java层的InputQueue来处理这个键盘事件了: env->CallStaticVoidMethod(gInputQueueClassInfo.clazz, dispatchMethodId,inputHandlerObjLocal,inputEventObj, jlong(finishedToken)); 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966638,如需转载请自行联系原作者

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

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

Step 2.ViewRoot.requestLayout 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... publicvoidrequestLayout(){ ...... mLayoutRequested=true; scheduleTraversals(); } ...... } 这个函数调用了scheduleTraversals函数来进一步执行操作,由于篇幅关系,我们就不详细描述scheduleTraversals函数了,简单来说,在scheduleTraversals函数中,会通过sendEmptyMessage(DO_TRAVERSAL)发送一个消息到应用程序的消息队列中,这个消息最终由ViewRoot的handleMessage函数处理,而ViewRoot的handleMessage函数把这个消息交给ViewRoot类的performTraversals来处理,在performTraversals函数中,又会调用ViewRoot类的relayoutWindow函数来进一步执行操作,最后在relayoutWindow函数中,就会通过WindowManagerService内部类Session的远程接口sWindowSession的relayout函数来进入到WindowManagerService中。 Step 3.WindowManagerService.Session.relayout 这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中: publicclassWindowManagerServiceextendsIWindowManager.Stub implementsWatchdog.Monitor{ ...... privatefinalclassSessionextendsIWindowSession.Stub implementsIBinder.DeathRecipient{ ...... publicintrelayout(IWindowwindow,WindowManager.LayoutParamsattrs, intrequestedWidth,intrequestedHeight,intviewFlags, booleaninsetsPending,RectoutFrame,RectoutContentInsets, RectoutVisibleInsets,ConfigurationoutConfig,SurfaceoutSurface){ //Log.d(TAG,">>>>>>ENTEREDrelayoutfrom"+Binder.getCallingPid()); intres=relayoutWindow(this,window,attrs, requestedWidth,requestedHeight,viewFlags,insetsPending, outFrame,outContentInsets,outVisibleInsets,outConfig,outSurface); //Log.d(TAG,"<<<<<<EXITINGrelayoutto"+Binder.getCallingPid()); returnres; } ...... } ...... } 这个函数只是简单地调用WindowManagerService的成员函数relayoutWIndow来进一步处理。 Step 4.WindowManagerService.relayoutWIndow 这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中: publicclassWindowManagerServiceextendsIWindowManager.Stub implementsWatchdog.Monitor{ ...... publicintrelayoutWindow(Sessionsession,IWindowclient, WindowManager.LayoutParamsattrs,intrequestedWidth, intrequestedHeight,intviewVisibility,booleaninsetsPending, RectoutFrame,RectoutContentInsets,RectoutVisibleInsets, ConfigurationoutConfig,SurfaceoutSurface){ ...... synchronized(mWindowMap){ ...... mInputMonitor.updateInputWindowsLw(); } ...... } ...... } 这个函数又会继续调用mInputMonitor的updateInputWindowsLw成员函数来更新当前的输入窗口,mInputMonitor是WindowManagerService的成员变量,它的类型为InputMonitor。 Step 5.InputMonitor.updateInputWindowsLw 这个函数定义在frameworks/base/services/java/com/android/server/WindowManagerService.java文件中: publicclassWindowManagerServiceextendsIWindowManager.Stub implementsWatchdog.Monitor{ ...... finalclassInputMonitor{ ...... /*Updatesthecachedwindowinformationprovidedtotheinputdispatcher.*/ publicvoidupdateInputWindowsLw(){ //Populatetheinputwindowlistwithinformationaboutallofthewindowsthat //couldpotentiallyreceiveinput. //Asanoptimization,wecouldtrytoprunethelistofwindowsbutthisturns //outtobedifficultbecauseonlythenativecodeknowsforsurewhichwindow //currentlyhastouchfocus. finalArrayList<WindowState>windows=mWindows; finalintN=windows.size(); for(inti=N-1;i>=0;i--){ finalWindowStatechild=windows.get(i); if(child.mInputChannel==null||child.mRemoved){ //Skipthiswindowbecauseitcannotpossiblyreceiveinput. continue; } ...... //Addawindowtoourlistofinputwindows. finalInputWindowinputWindow=mTempInputWindows.add(); ...... } //Sendwindowstonativecode. mInputManager.setInputWindows(mTempInputWindows.toNullTerminatedArray()); ...... } ...... } ...... } 这个函数将当前系统中带有InputChannel的Activity窗口都设置为InputManager的输入窗口,但是后面我们会看到,只有当前激活的窗口才会响应键盘消息。 Step 6.InputManager.setInputWindows 这个函数定义在frameworks/base/services/java/com/android/server/InputManager.java文件中: publicclassInputManager{ ...... publicvoidsetInputWindows(InputWindow[]windows){ nativeSetInputWindows(windows); } ...... } 这个函数调用了本地方法nativeSetInputWindows来进一步执行操作。 Step 7.InputManager.nativeSetInputWindows 这个函数定义在frameworks/base/services/jni/com_android_server_InputManager.cpp文件中: staticvoidandroid_server_InputManager_nativeSetInputWindows(JNIEnv*env,jclassclazz, jobjectArraywindowObjArray){ if(checkInputManagerUnitialized(env)){ return; } gNativeInputManager->setInputWindows(env,windowObjArray); } 这里的gNativeInputManager我们前面分析InputManager的启动过程时已经见过了,这是一个本地InputManager对象,通过它进一步设置当前系统的输入窗口。 本文转自 Luoshengyang 51CTO博客,原文链接:xxxxxxx,如需转载请自行联系原作者

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

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

函数首先根据文件名来打开这个设备文件: fd=open(deviceName,O_RDWR); 系统中所有输入设备文件信息都保存在成员变量mDevicesById中,因此,先在mDevicesById找到一个空位置来保存当前打开的设备文件信息: mDevicesById[devid].seq=(mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK; if(mDevicesById[devid].seq==0){ mDevicesById[devid].seq=1<<SEQ_SHIFT; } 找到了空闲位置后,就为这个输入设备文件创建相应的device_t信息: 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中: 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; } } } 如果是的话,还要继续进一步初始化前面为这个设备文件所创建的device_t结构体,主要就是把结构体device的classes成员变量的INPUT_DEVICE_CLASS_KEYBOARD位置为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); } 到这里,系统中的输入设备文件就打开了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966619,如需转载请自行联系原作者

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

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

Step 36. ViewRoot.finishInputEvent 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... privatevoidfinishInputEvent(){ ...... if(mFinishedCallback!=null){ mFinishedCallback.run(); mFinishedCallback=null; }else{ ...... } } ...... } ViewRoot类里面的成员变量mFinishedCallback是在前面Step 25中由InputQueue设置的,它是一个Runnable对象,实际类型是定义在InputQueue的内部类FinishedCallback,因此,这里调用它的run方法时,接下来就会调用InputQueue的内部类FinishedCallback的run成员函数: publicfinalclassInputQueue{ ...... privatestaticclassFinishedCallbackimplementsRunnable{ ...... publicvoidrun(){ synchronized(sLock){ ...... nativeFinished(mFinishedToken); ...... } } ...... } ...... } 这里它调用外部类InputQueue的本地方法nativeFinished来进一步处理。 Step 37. InputQueue.nativeFinished 这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中: staticvoidandroid_view_InputQueue_nativeFinished(JNIEnv*env,jclassclazz, jlongfinishedToken){ status_tstatus=gNativeInputQueue.finished( env,finishedToken,false/*ignoreSpuriousFinish*/); ...... } 这个函数只是简单只调用NativeInputQueue的finished方法来进一处处理。 Step 38. NativeInputQueue.finished 这个函数定义在frameworks/base/core/jni/android_view_InputQueue.cpp文件中: status_tNativeInputQueue::finished(JNIEnv*env,jlongfinishedToken,boolignoreSpuriousFinish){ int32_treceiveFd; uint16_tconnectionId; uint16_tmessageSeqNum; parseFinishedToken(finishedToken,&receiveFd,&connectionId,&messageSeqNum); {//acquirelock AutoMutex_l(mLock); ssize_tconnectionIndex=mConnectionsByReceiveFd.indexOfKey(receiveFd); ...... sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex); ...... connection->messageInProgress=false; status_tstatus=connection->inputConsumer.sendFinishedSignal(); ...... }//releaselock returnOK; } 这个函数最重要的参数便是finishedToken了,通过它可以获得之前通知Java层的InputQueue类来处理键盘事件的Connection对象,它的值是在上面的Step 21(NativeInputQueue.handleReceiveCallback)中生成的: finishedToken=generateFinishedToken(receiveFd,connection->id,connection->messageSeqNum); 函数generateFinishedToken的定义如下: jlongNativeInputQueue::generateFinishedToken(int32_treceiveFd,uint16_tconnectionId, uint16_tmessageSeqNum){ return(jlong(receiveFd)<<32)|(jlong(connectionId)<<16)|jlong(messageSeqNum); } 它的实现很简单,只是把receiveFd(前向管道的读端文件描述符)、connectionId(Client端的InputChannel对应的Connection对象在NativeInputQueue中的索引)和messageSeqNum(键盘消息的序列号)三个数值通过移位的方式编码在一个jlong值里面,即编码在上面的finishedToken参数里面。 因此,在上面的finished函数里面,首先就是要对参数值finishedToken进行解码,把receiveFd、connectionId和messageSeqNum三个值分别取回来: parseFinishedToken(finishedToken,&receiveFd,&connectionId,&messageSeqNum); parseFinishedToken的定义如下: voidNativeInputQueue::parseFinishedToken(jlongfinishedToken, int32_t*outReceiveFd,uint16_t*outConnectionId,uint16_t*outMessageIndex){ *outReceiveFd=int32_t(finishedToken>>32); *outConnectionId=uint16_t(finishedToken>>16); *outMessageIndex=uint16_t(finishedToken); } 有了这个receiveFd和connectionId之后,就可以把相应的Connection对象取回来了: ssize_tconnectionIndex=mConnectionsByReceiveFd.indexOfKey(receiveFd); ...... sp<Connection>connection=mConnectionsByReceiveFd.valueAt(connectionIndex); 接下来就是调用这个connection对象中的inputConsumer对象来发送信号通知Server端的InputChannel,应用程序这一侧处理完刚才发生的键盘事件了: status_tstatus=connection->inputConsumer.sendFinishedSignal(); 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966645,如需转载请自行联系原作者

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

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

Step 39. InputConsumer.sendFinishedSignal 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputConsumer::sendFinishedSignal(){ ...... returnmChannel->sendSignal(INPUT_SIGNAL_FINISHED); } 这个函数的实现很简单,只是调用其内部对象mChannel的sendSignal函数来执行发送信号的通知。前面我们已经说过,这里的mChannel的类型为InputChannel,它是注册在应用程序一侧的Client端InputChannel,它的成员函数sendSignal的定义我们在上面的Step 20中已经分析过了,这里不再详述,不过,这里和上面Step 20不一样的地方是,它里的通知方向是从反向管道的写端(在应用程序这一侧)到反向管道的读端(在InputDispatcher这一侧)。 前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18(InputDispatcher.registerInputChannel)中,说到InputDispatcher把一个反向管道的读端文件描述符添加到WindowManagerService所运行的线程中的Looper对象中去,然后就会在这个反向管道的读端上睡眠等待有这个管道有新的内容可读。现在,InputConsumer往这个反向管道写入新的内容了,于是,InputDispatcher就被唤醒过来了,唤醒过来后,它所调用的函数是InputDispatcher.handleReceiveCallback函数,这与前面的Step 21的逻辑是一样的。 Step 40. InputDispatcher.handleReceiveCallack 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: intInputDispatcher::handleReceiveCallback(intreceiveFd,intevents,void*data){ InputDispatcher*d=static_cast<InputDispatcher*>(data); {//acquirelock AutoMutex_l(d->mLock); ssize_tconnectionIndex=d->mConnectionsByReceiveFd.indexOfKey(receiveFd); ...... nsecs_tcurrentTime=now(); sp<Connection>connection=d->mConnectionsByReceiveFd.valueAt(connectionIndex); ...... status_tstatus=connection->inputPublisher.receiveFinishedSignal(); if(status){ ...... return0;//removethecallback } d->finishDispatchCycleLocked(currentTime,connection); ...... return1; }//releaselock } 这个函数首先是通过传进来的receiveFd参数(反向管道的读端文件描述符)的值取得相应的Connection对象: ssize_tconnectionIndex=d->mConnectionsByReceiveFd.indexOfKey(receiveFd); ...... sp<Connection>connection=d->mConnectionsByReceiveFd.valueAt(connectionIndex); 然后通过调用这个connection对象的内部对象inputPublisher的receiveFinishedSignal函数来确认是否真的收到键盘事件处理完成的信号,确认之后,就会调用InputDispatcher对象d的finishDispatchCycleLocked函数来执行一些善后工作。下面我们就依次分析这两个过程。 Step 41. InputPublisher.receiverFinishedSignal 这个函数定义在frameworks/base/libs/ui/InputTransport.cpp文件中: status_tInputPublisher::receiveFinishedSignal(){ .... charsignal; status_tresult=mChannel->receiveSignal(&signal); if(result){ returnresult; } if(signal!=INPUT_SIGNAL_FINISHED){ ....... returnUNKNOWN_ERROR; } returnOK; } 这里的逻辑和前面的Step 22中NativeInputQueue确认是否真的收到键盘事件分发的信号的逻辑是一致的,都是通过InputChannel的receiveSignal函数来确认是否在管道中收到了某一个约定的字符值,不过,这里约定的字符值为INPUT_SIGNAL_FINISHED。 回到前面的Step 40中,确认了是真的收到了键盘事件处理完成的信号后,就调用InputDispatcher的finishDispatchCycleLocked函数来执行一些善后工作了。 Step 42. InputDispatcher.finishDispatchCycleLocked 这个函数定义在frameworks/base/libs/ui/InputDispatcher.cpp文件中: voidInputDispatcher::finishDispatchCycleLocked(nsecs_tcurrentTime, constsp<Connection>&connection){ ...... //Notifyothersystemcomponents. onDispatchCycleFinishedLocked(currentTime,connection); //Resetthepublishersincetheeventhasbeenconsumed. //Wedothisnowsothatthepublishercanreleasesomeofitsinternalresources //whilewaitingforthenextdispatchcycletobegin. status_tstatus=connection->inputPublisher.reset(); ...... startNextDispatchCycleLocked(currentTime,connection); } 这个函数主要就是做了三件事情: 一是通知其它系统,InputDispatcher完成了一次键盘事件的处理: //Notifyothersystemcomponents. onDispatchCycleFinishedLocked(currentTime,connection); 二是调用相应的connection对象的内部对象inputPublisher来的reset函数来回收一些资源,它里面其实就是释放前面在Step 18(InputPublisher.publishKeyEvent)使用的匿名共享内存了: //Resetthepublishersincetheeventhasbeenconsumed. //Wedothisnowsothatthepublishercanreleasesomeofitsinternalresources //whilewaitingforthenextdispatchcycletobegin. status_tstatus=connection->inputPublisher.reset(); 三是调用InputDispatcher的startNextDispatchCycleLocked函数来处理下一个键盘事件: startNextDispatchCycleLocked(currentTime,connection); 因为正在处理当前这个键盘事件的时候,很有可能又同时发生了其它的键盘事件,因此,这里InputDispatcher还不能停下来,需要继续调用startNextDispatchCycleLocked继续处理键盘事件,不过下一个键盘事件的处理过程和我们现在分析的过程就是一样的了。 至此,InputManager分发键盘消息给应用程序的过程就分析完成了,这是一个比较复杂的过程,不过,只要我们抓住主要的线索,就不难理解了,现在我们就小结一下这个过程的四个主要线索: A. 键盘事件发生,InputManager中的InputReader被唤醒,此前InputReader睡眠在/dev/input/event0这个设备文件上; B.InputReader被唤醒后,它接着唤醒InputManager中的InputDispatcher,此前InputDispatcher睡眠在InputManager所运行的线程中的Looper对象里面的管道的读端上; C. InputDispatcher被唤醒后,它接着唤醒应用程序的主线程来处理这个键盘事件,此前应用程序的主线程睡眠在Client端InputChannel中的前向管道的读端上; D. 应用程序处理处理键盘事件之后,它接着唤醒InputDispatcher来执行善后工作,此前InputDispatcher睡眠在Server端InputChannel的反向管道的读端上,注意这里与第二个线索处的区别。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966648,如需转载请自行联系原作者

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

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

4.应用程序注销键盘消息接收通道的过程分析 当Activity窗口创建时,它会向InputManager注册键盘消息接收通道,而当Activity窗口销毁时,它就会向InputManager注销前面注册的键盘消息接收通道了,本节内容就来看看应用程序注销键盘消息接收通道的过程。 当我们按下键盘上的Back键时,当前激活的Activity窗口就会被失去焦点,但是这时候它还没有被销毁,它的状态被设置为Stopped;当新的Activity窗口即将要显示时,它会通知WindowManagerService,这时候WindowManagerService就会处理当前处理Stopped状态的Activity窗口了,要执行的操作就是销毁它们了,在销毁的时候,就会注销它们之前所注册的键盘消息接收通道。 新的Activity窗口通知WindowManagerService它即将要显示的过程比较复杂,但是它与我们本节要介绍的内容不是很相关,因此,这里就略过大部过程了,我们从ActvitiyRecord的windowsVisible函数开始分析。注意,这里的ActivityRecord是新的Activity窗口在ActivityManangerService的代表,而那些处于Stopped状态的Activity窗口 会放在ActivityStack类的一个等待可见的mWaitingVisibleActivities列表里面,事实于,对于那些Stopped状态的Activity窗口来说,它们是等待销毁,而不是等待可见。 像前面一样,我们先来看一张应用程序注销键盘消息接收通道的过程的序列图,然后根据这个序列图来详细分析互一个步骤: Step 1. ActivityRecord.windowsVisible 这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityRecord.java文件中: classActivityRecordextendsIApplicationToken.Stub{ ...... booleannowVisible;//isthisactivity'swindowvisible? booleanidle;//hastheactivitygoneidle? ...... publicvoidwindowsVisible(){ synchronized(service){ ...... if(!nowVisible){ nowVisible=true; if(!idle){ ....... }else{ //Ifthisactivitywasalreadyidle,thenwenowneedto //makesureweperformthefullstopofanyactivities //thatarewaitingtodoso.Thisisbecausewewon't //dothatwhiletheyarestillwaitingforthisoneto //becomevisible. finalintN=stack.mWaitingVisibleActivities.size(); if(N>0){ for(inti=0;i<N;i++){ ActivityRecordr=(ActivityRecord) stack.mWaitingVisibleActivities.get(i); r.waitingVisible=false; ...... } stack.mWaitingVisibleActivities.clear(); Messagemsg=Message.obtain(); msg.what=ActivityStack.IDLE_NOW_MSG; stack.mHandler.sendMessage(msg); } } ...... } } } ...... } 应用程序中的每一个Activity在ActivityManagerService都有一个代表ActivityRecord,它们以堆栈的形式组织在ActivityManaerService中的ActivityStack中。一个即将要显示,但是还没有显示的Activity,它在ActivityManagerService中的ActivityRecord的成员变量nowVisible为false,而成员变量idle为ture,表示这个即将要显示的Activity窗口处于空闲状态。因此,在上面的这个函数中,会执行下面的语句: finalintN=stack.mWaitingVisibleActivities.size(); if(N>0){ for(inti=0;i<N;i++){ ActivityRecordr=(ActivityRecord) stack.mWaitingVisibleActivities.get(i); r.waitingVisible=false; ...... } stack.mWaitingVisibleActivities.clear(); Messagemsg=Message.obtain(); msg.what=ActivityStack.IDLE_NOW_MSG; stack.mHandler.sendMessage(msg); } 前面我们说过,当用户按下键盘上的Back键时,当前激活的Activity记录就被放在ActivityStack对象stack的成员变量mWaitingVisibleActivities中了,这时候就要对它进行处理了。首先是将它们的Activity记录的waitingVisible设置为false,然后就把它们从ActivityStack对象stack的成员变量mWaitingVisibleActivities清空,最后向ActivityStack对象stack发送一个ActivityStack.IDLE_NOW_MSG消息。这个消息最终是由ActivityStack类的activityIdleInternal函数来处理的。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966649,如需转载请自行联系原作者

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

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

Step 22. poll 这是一个Linux系统的文件操作系统调用,它用来查询指定的文件列表是否有有可读写的,如果有,就马上返回,否则的话,就阻塞线程,并等待驱动程序唤醒,重新调用poll函数,或超时返回。在我们的这个场景中,就是要查询是否有键盘事件发生,如果有的话,就返回,否则的话,当前线程就睡眠等待键盘事件的发生了。 这样,InputManager的启动过程就分析完了,下面我们再分析应用程序注册键盘消息接收通道的过程。 2.应用程序注册键盘消息接收通道的过程分析 InputManager启动以后,就开始负责监控键盘输入事件了。当InputManager监控到键盘输入事件时,它应该把这个键盘事件分发给谁呢?当然是要把这个键盘消息分发给当前激活的Activity窗口了,不过,当前激活的Activity窗口还需要主动注册一个键盘消息接收通道到InputManager中去,InputManager才能把这个键盘消息分发给它处理。那么,当前被激活的Activity窗口又是什么时候去注册这个键盘消息接收通道的呢?在前面一篇文章Android应用程序启动过程源代码分析中,我们分析Android应用程序的启动过程时,在Step 33中分析到ActivityThread类的handleLaunchActivity函数中,我们曾经说过,当函数handleLaunchActivity调用performLaunchActivity函数来加载这个完毕应用程序的默认Activity后,再次回到handleLaunchActivity函数时,会调用handleResumeActivity函数来使这个Activity进入Resumed状态。在调用handleResumeActivity函数的过程中,ActivityThread会通过android.view.WindowManagerImpl类为该Activity创建一个ViewRoot实例,并且会通过调用ViewRoot类的setView成员函数把与该Activity关联的View设置到这个ViewRoot中去,而Activity正是通过ViewRoot类的setView成员函数来注册键盘消息接收通道的。 有了这些背影知识后,接下来,我们就可以从ViewRoot.setView函数开始分析应用程序注册键盘消息接收通道的过程了。首先看一下这个注册过程的序列图,然后再详细分析每一个步骤: Step 1. ViewRoot.setView 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... publicvoidsetView(Viewview,WindowManager.LayoutParamsattrs, ViewpanelParentView){ ...... synchronized(this){ if(mView==null){ ...... //Schedulethefirstlayout-before-addingtothewindow //manager,tomakesurewedotherelayoutbeforereceiving //anyothereventsfromthesystem. requestLayout(); mInputChannel=newInputChannel(); try{ res=sWindowSession.add(mWindow,mWindowAttributes, getHostVisibility(),mAttachInfo.mContentInsets, mInputChannel); }catch(RemoteExceptione){ ...... }finally{ ...... } ...... if(viewinstanceofRootViewSurfaceTaker){ mInputQueueCallback= ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue(); } if(mInputQueueCallback!=null){ mInputQueue=newInputQueue(mInputChannel); mInputQueueCallback.onInputQueueCreated(mInputQueue); }else{ InputQueue.registerInputChannel(mInputChannel,mInputHandler, Looper.myQueue()); } ...... } } } } 这个函数中与注册键盘消息接收通道(InputChannel)相关的逻辑主要有三处,一是调用requestLayout函数来通知InputManager,这个Activity窗口是当前被激活的窗口,二是调用sWindowSession(WindowManagerService内部类Session的远程接口)的add成员函数来把键盘消息接收通道的一端注册在InputManager中,三是调用InputQueue的registerInputChannel成员函数来把键盘消息接收通道的另一端注册在本应用程序的消息循环(Looper)中。这样,当InputManager监控到有键盘消息时,就会先找到当前被激活的窗口,然后找到其在InputManager中对应的键盘消息接收通道,通过这个通道在InputManager中的一端来通知在应用程序消息循环中的另一端,就把键盘消息分发给当前激活的Activity窗口了。 在接下来的内容中,我们首先描述requestLayout函数是如何告诉InputManager当前的Activity窗口便是激活窗口的,接着再回过头来分析应用程序是如何把键盘消息接收通道的一端注册到InputManager中去的,最后分析应用程序是如何键盘消息接收通道的另一端注册到本应用程序的消息循环中去了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966622,如需转载请自行联系原作者

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

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

Step 30.ViewRoot.dispatchFinishedEvent 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... publicvoiddispatchFinishedEvent(intseq,booleanhandled){ Messagemsg=obtainMessage(FINISHED_EVENT); msg.arg1=seq; msg.arg2=handled?1:0; sendMessage(msg); } ...... } 和前面的Step 26一样,ViewRoot不是直接处理这个键盘事件,而是把它作为一个消息(FINISHED_EVENT)放在消息队列中去,最后,这个消息由ViewRoot的handleFinishedEvent函数来处理。 Step 31.ViewRoot.handleFinishedEvent 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... voidhandleFinishedEvent(intseq,booleanhandled){ finalKeyEventevent=(KeyEvent)retrievePendingEvent(seq); ...... if(event!=null){ finalbooleansendDone=seq>=0; if(!handled){ deliverKeyEventToViewHierarchy(event,sendDone); return; }elseif(sendDone){ ...... }else{ ...... } } } ...... } 如果InputMethodManager没有处理这个键盘事件,那么ViewRoot就会调用deliverKeyEventToViewHierarchy函数来把这个键盘事件分发给当前激活的Activity窗口来处理。 Step 32.ViewRoot.deliverKeyEventToViewHierarchy 这个函数定义在frameworks/base/core/java/android/view/ViewRoot.java文件中: publicfinalclassViewRootextendsHandlerimplementsViewParent, View.AttachInfo.Callbacks{ ...... privatevoiddeliverKeyEventToViewHierarchy(KeyEventevent,booleansendDone){ try{ if(mView!=null&&mAdded){ ...... booleankeyHandled=mView.dispatchKeyEvent(event); } ...... }finally{ if(sendDone){ finishInputEvent(); } } } ...... } 这个函数首先会调用ViewRoot类的成员变量mView的dispatchKeyEvent来处理这个键盘事件,然后最调用ViewRoot类的finishInputEvent来处理手尾工作。 ViewRoot类的成员变量mView的类型为DecorView,它是由ActivityThread类第一次Resume当前的Activity窗口时创建的,具体可以参考ActivityThread类的handleResumeActivity成员函数,这里就不关注了。 Step 33. DecorView.dispatchKeyEvent 这个函数定义在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java文件中,它是PhoneWindow类的一个内部类: publicclassPhoneWindowextendsWindowimplementsMenuBuilder.Callback{ ...... privatefinalclassDecorViewextendsFrameLayoutimplementsRootViewSurfaceTaker{ ...... @Override publicbooleandispatchKeyEvent(KeyEventevent){ ...... finalCallbackcb=getCallback(); finalbooleanhandled=cb!=null&&mFeatureId<0?cb.dispatchKeyEvent(event) :super.dispatchKeyEvent(event); ...... } ...... } ...... } 这里通过getCallback函数返回的是当前应用程序的激活的Activity窗口的Window.Callback接口,一般它不为NULL,因此,这个函数会调用Activity类的dispatchKeyEvent来处理这个键盘事件。 Step 34. Activity.dispatchKeyEvent 这个函数定义在frameworks/base/core/java/android/app/Activity.java文件中: publicclassActivityextendsContextThemeWrapper implementsLayoutInflater.Factory, Window.Callback,KeyEvent.Callback, OnCreateContextMenuListener,ComponentCallbacks{ ...... publicbooleandispatchKeyEvent(KeyEventevent){ ...... Viewdecor=mDecor; if(decor==null)decor=win.getDecorView(); returnevent.dispatch(this,decor!=null ?decor.getKeyDispatcherState():null,this); } ...... } 这里,Activity不是直接处理这个键盘事件,而是通过KeyEvent的dispatch转发一下。注意,KeyEvent的成中函数dispatch的第一个参数的类型是KeyEvent.Callback,而Activity实现了这个接口,因此,这里可以传this引用过去。 Step 35. KeyEvent.dispatch 这个函数定义在frameworks/base/core/java/android/view/KeyEvent.java文件中: publicclassKeyEventextendsInputEventimplementsParcelable{ ...... publicfinalbooleandispatch(Callbackreceiver,DispatcherStatestate, Objecttarget){ switch(mAction){ caseACTION_DOWN:{ ...... booleanres=receiver.onKeyDown(mKeyCode,this); ...... returnres; } caseACTION_UP: ...... returnreceiver.onKeyUp(mKeyCode,this); caseACTION_MULTIPLE: finalintcount=mRepeatCount; finalintcode=mKeyCode; if(receiver.onKeyMultiple(code,count,this)){ returntrue; } ...... returnfalse; } returnfalse; } ...... } 这里就根据一个键是按下(ACTION_DOWN)、还是松开(ACTION_UP)或者是一个相同的键被多次按下和松开(ACTION_MULTIPLE)等不同事件类型来分别调用Activity的onKeyDown、onKeyUp和onKeyMultiple函数了。 Activity窗口处理完这个键盘事件后,层层返回,最后回到Step 32中,调用finishInputEvent事件来处理一些手尾工,下面我们将会看到这些手尾工是什么。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/966644,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

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

Sublime Text

Sublime Text

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

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册