Android编程输入事件流程详解
本文实例讲述了Android编程输入事件流程。分享给大家供大家参考,具体如下:
EventHub对输入设备进行了封装。输入设备驱动程序对用户空间应用程序提供一些设备文件,这些设备文件放在/dev/input里面。
EventHub扫描/dev/input下所有设备文件,并打开它们。
boolEventHub::openPlatformInput(void)
{
...
mFDCount=1;
mFDs=(pollfd*)calloc(1,sizeof(mFDs[0]));
mDevices=(device_t**)calloc(1,sizeof(mDevices[0]));
mFDs[0].events=POLLIN;
mDevices[0]=NULL;
res=scan_dir(device_path);
...
returntrue;
}
EventHub对外提供了一个函数用于从输入设备文件中读取数据。
boolEventHub::getEvent(int32_t*outDeviceId,int32_t*outType,
int32_t*outScancode,int32_t*outKeycode,uint32_t*outFlags,
int32_t*outValue,nsecs_t*outWhen)
{
...
while(1){
//First,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;
*outDeviceId=device->id;
if(*outDeviceId==mFirstKeyboardId)*outDeviceId=0;
*outType=DEVICE_REMOVED;
deletedevice;
returntrue;
}
if(mOpeningDevices!=NULL){
device_t*device=mOpeningDevices;
LOGV("Reportingdeviceopened:id=0x%x,name=%s\n",
device->id,device->path.string());
mOpeningDevices=device->next;
*outDeviceId=device->id;
if(*outDeviceId==mFirstKeyboardId)*outDeviceId=0;
*outType=DEVICE_ADDED;
returntrue;
}
release_wake_lock(WAKE_LOCK_ID);
pollres=poll(mFDs,mFDCount,-1);
acquire_wake_lock(PARTIAL_WAKE_LOCK,WAKE_LOCK_ID);
if(pollres<=0){
if(errno!=EINTR){
LOGW("selectfailed(errno=%d)\n",errno);
usleep(100000);
}
continue;
}
for(i=1;i<mFDCount;i++){
if(mFDs[i].revents){
LOGV("reventsfor%d=0x%08x",i,mFDs[i].revents);
if(mFDs[i].revents&POLLIN){
res=read(mFDs[i].fd,&iev,sizeof(iev));
if(res==sizeof(iev)){
LOGV("%sgot:t0=%d,t1=%d,type=%d,code=%d,v=%d",
mDevices[i]->path.string(),
(int)iev.time.tv_sec,(int)iev.time.tv_usec,
iev.type,iev.code,iev.value);
*outDeviceId=mDevices[i]->id;
if(*outDeviceId==mFirstKeyboardId)*outDeviceId=0;
*outType=iev.type;
*outScancode=iev.code;
if(iev.type==EV_KEY){
err=mDevices[i]->layoutMap->map(iev.code,outKeycode,outFlags);
LOGV("iev.code=%doutKeycode=%doutFlags=0x%08xerr=%d\n",
iev.code,*outKeycode,*outFlags,err);
if(err!=0){
*outKeycode=0;
*outFlags=0;
}
}else{
*outKeycode=iev.code;
}
*outValue=iev.value;
*outWhen=s2ns(iev.time.tv_sec)+us2ns(iev.time.tv_usec);
returntrue;
}else{
if(res<0){
LOGW("couldnotgetevent(errno=%d)",errno);
}else{
LOGE("couldnotgetevent(wrongsize:%d)",res);
}
continue;
}
}
}
}
...
}
对于按键事件,调用mDevices[i]->layoutMap->map进行映射。映射实际是由KeyLayoutMap::map完成的,KeyLayoutMap类里读取配置文件qwerty.kl,由配置文件qwerty.kl决定键值的映射关系。你可以通过修改./development/emulator/keymaps/qwerty.kl来改变键值的映射关系。
JNI函数
在frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文件中,向JAVA提供了函数android_server_KeyInputQueue_readEvent,用于读取输入设备事件。
staticjboolean
android_server_KeyInputQueue_readEvent(JNIEnv*env,jobjectclazz,jobjectevent)
{
gLock.lock();
sphub=gHub;
if(hub==NULL){
hub=newEventHub;
gHub=hub;
}
gLock.unlock();
int32_tdeviceId;
int32_ttype;
int32_tscancode,keycode;
uint32_tflags;
int32_tvalue;
nsecs_twhen;
boolres=hub->getEvent(&deviceId,&type,&scancode,&keycode,&flags,&value,&when);
env->SetIntField(event,gInputOffsets.mDeviceId,(jint)deviceId);
env->SetIntField(event,gInputOffsets.mType,(jint)type);
env->SetIntField(event,gInputOffsets.mScancode,(jint)scancode);
env->SetIntField(event,gInputOffsets.mKeycode,(jint)keycode);
env->SetIntField(event,gInputOffsets.mFlags,(jint)flags);
env->SetIntField(event,gInputOffsets.mValue,value);
env->SetLongField(event,gInputOffsets.mWhen,(jlong)(nanoseconds_to_milliseconds(when)));
returnres;
}
readEvent调用hub->getEvent读了取事件,然后转换成JAVA的结构。
事件中转线程
在frameworks/base/services/java/com/android/server/KeyInputQueue.java里创建了一个线程,它循环的读取事件,然后把事件放入事件队列里。
ThreadmThread=newThread("InputDeviceReader"){
publicvoidrun(){
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
try{
RawInputEventev=newRawInputEvent();
while(true){
InputDevicedi;
readEvent(ev);
send=preprocessEvent(di,ev);
addLocked(di,curTime,ev.flags,...,me);
}
}
};
}
输入事件分发线程
在frameworks/base/services/java/com/android/server/WindowManagerService.java里创建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。
mQueue.getEvent dispatchKey/dispatchPointer/dispatchTrackball
更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android多媒体操作技巧汇总(音频,视频,录音等)》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》
希望本文所述对大家Android程序设计有所帮助。