代码是基于android4.1的。
1recovery输入事件及处理分析
1.1时序图
1.2代码分析
1.2.1 输入事件初始化
Recovery的入口是recovery.cpp中的main函数,当然会根据参数的不同,进入recovery的模式也就不一样,这里我们就不一一介绍了,我们这里主要看图形界面模式,即有个人机交互的见面,用户可以通过按键选择不同的执行操作。
根据上面的时序图中,我们可以看到,在main函数中,需要做一些界面显示、输入事件的初始化工作。而在这里,我们就主要先关注输入事件的初始化工作,即在main函数中调用了ui.cpp的Init()方法,下面看看其代码:
void RecoveryUI::Init(){ } |
在该方法中,主要完成了两个动作,第一就是初始化话输入事件,注册了回调函数,当有输入事件的时候回调,第二就是创建了一个新的线程,用于读取输入事件的数据。
我们先看看events.c中的ev_init()方法,输入事件初始化,代码如下:
int ev_init(ev_callbackinput_cb, void *data) { } #define test_bit(bit, array) \ |
代码看起来还是非常简单的,输入设备的设备节点都在/dev/input/目录下,所以需要扫面下面所有的设备节点。
调用openat()打开设备节点,这是linux的系统调用,这里就不说了。
调用ioctl的,并用宏EVIOCGBIT产生参数,获取一个设备的特性,这里特性会说明该设备是按键设备还是触摸设备等,并将特性存在中数组ev_bits中。
定义了test_bit的宏,用于判断ev_bits中的特性是否是我们想要的,在linux input系统中,EV_KEY指的是按键设备,EV_REL值相对坐标,如光标移动。看到这里了,如果我们想要接受触摸消息,那么我将在这里添加EV_ABS的支持。后续会说明添加具体方法。
当打开的设备是我们想要监听的设备的时候,我们将设备节点的文件描述符等信息添加到ev_fds结构体数组中,还有将注册的回调函数input_cb添加到结构体数据ev_fdinfo中。
到此就完成了输入事件的初始化,接下来就看在创建的新线程中读取输入事件。
1.2.2 创建线程读取输入事件
在前面的RecoveryUI::Init()方法中,我们看到了这么一句:
|
调用了pthread_create()方法创建新的线程,该方法的原型如下:
int pthread_create(pthread_t*restricttidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
若成功则返回0,否则返回出错编号
返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于制定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
linux下用C开发多线程程序,Linux系统下的多线程遵循POSIX线程接口,称为pthread。 由restrict修饰的指针是最初唯一对指针所指向的对象进行存取的方法,仅当第二个指针基于第一个时,才能对对象进行存取。对对象的存取都限定于基于由restrict修饰的指针表达式中。由restrict修饰的指针主要用于函数形参,或指向由malloc()分配的内存空间。restrict数据类型不改变程序的语义。编译器能通过作出restrict修饰的指针是存取对象的唯一方法的假设,更好地优化某些类型的例程。下面看四个参数:
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
第四个参数是运行函数的参数。
另外,在编译时注意加上-lpthread参数,以调用链接库。因为pthread并非Linux系统的默认库。
在我们这里用到的代码中,第一个参数的定义为:pthread_t input_t;为指向线程标识符的指针。接下来我们主要看新线程的入口函数input_thread()方法,代码如下:
void*RecoveryUI::input_thread(void *cookie) { } |
在一个循环里面不断的查询是否有输入事件,那么,我们看看events.c中的ev_wait()方法,代码如下:
int ev_wait(int timeout) { } |
还记得在输入事件初始化的时候,将按键等我们想监听的设备点信息添加到了ev_fds结构体中,在这里我们将用到了。
系统调用poll()方法,如果ev_fds包含的设备节点中有消息,那么返回的值r将大于0,所以ev_wait()方法的返回值为0。再回头看看input_thread()方法,当ev_wait()返回值等于零的时候,将调用ev_dispatch()派发输入消息。那么我们看看events.c中的该方法代码: