Android输入系统(一)
来源:互联网 发布:iphone吉他调音软件 编辑:程序博客网 时间:2024/06/05 04:38
Android输入系统(一)
首先我们明白,在PC或者手机上我们都支持热插拔,比如现在有个键盘,现在键盘插入USB接口,就会检测到。那么系统怎么知道有设备插入,又怎么识别这个设备呢?
- Android输入系统一
- 设备识别的两种方式
- 储备知识一监测文件
- 使用inotify函数流程
- 测试方法
- 使用epoll函数流程
- 测试方法
- 储备知识二通信
- 使用socketpair函数流程
- 测试方法
设备识别的两种方式
一.hotplug机制 1.内核发现键盘接入或者拔出 2.启动一个进程(hotplug进程) 3.进程发送消息告诉输入系统 4.这个输入系统处理这个消息二.inotify机制 系统检测/dev/input目录 发生变化进行对应处理
当然Android系统就是使用inotify机制,而使用inotify机制的核心是:
- inotify函数(用于监测目录中文件是否有删除,增加等)
- epoll函数(用于监测多个文件内容的变化)
- 有无数据读入
- 有无数据读出
储备知识一(监测文件)
使用inotify函数流程
@(参考frameworks\native\services\inputflinger\EventHub.cpp)
- fd=inotify_init()—用于初始化
- inotify_add_watch(目录名称,监测类型)—用于查看什么东西
- read(fd,…)—用于读取监测结果,返回值是inotify_event结构体
#include <unistd.h>#include <stdio.h>#include <sys/inotify.h>#include <string.h>#include <errno.h>int read_process_inotify_fd(int fd){ int res; char event_buf[512]; int event_size; int event_pos = 0; struct inotify_event *event; /* 此时read函数为阻塞式 */ res = read(fd, event_buf, sizeof(event_buf)); if(res < (int)sizeof(*event)) { if(errno == EINTR) return 0; printf("could not get event, %s\n", strerror(errno)); return -1; } /* * 读到的数据是1个或多个inotify_event * 由于结构体的长度不一样 * 所以逐个处理 */ while(res >= (int)sizeof(*event)) { event = (struct inotify_event *)(event_buf + event_pos); if(event->len) { if(event->mask & IN_CREATE) { printf("create file: %s\n", event->name); } else { printf("delete file: %s\n", event->name); } } event_size = sizeof(*event) + event->len; res -= event_size; event_pos += event_size; } return 0;}int main(int argc, char **argv){ int mINotifyFd; int result; if (argc != 2) { printf("Usage: %s <dir>\n", argv[0]); return -1; } /* inotify_init */ mINotifyFd = inotify_init(); /* add watch */ result = inotify_add_watch(mINotifyFd, argv[1], IN_DELETE | IN_CREATE); /* read */ while (1) { read_process_inotify_fd(mINotifyFd); } return 0;}
测试方法
$ gcc -o inotify inotify.c$ mkdir tmp$ ./inotify tmp &$ echo > tmp/1$ echo > tmp/2$ rm tmp/1 tmp/2
使用epoll函数流程
@(参考frameworks\native\services\inputflinger\EventHub.cpp)
- epoll_create()—创建fd
- 对每个文件执行epoll_ctl(…,EPOLL_CTL_ADD,…)—表示监测
- 执行epoll_wait(等待某个文件可用)
- 不再想监测某文件,可以执行epoll_ctl(…,EPOLL_CTL_DEL,…)
#include <sys/epoll.h>#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <string.h>#if 0typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64;} epoll_data_t;#endif#define DATA_MAX_LEN 500/* usage: epoll <file1> [file2] [file3] ... */int add_to_epoll(int fd, int epollFd){ int result; struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN;//当文件有数据的时候就可以监测到 eventItem.data.fd = fd; result = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &eventItem); return result;}/*从epoll中删除需要监测的文件*/void rm_from_epoll(int fd, int epollFd){ epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL);}int main(int argc, char **argv){ int mEpollFd; int i; char buf[DATA_MAX_LEN]; // Maximum number of signalled FDs to handle at a time. static const int EPOLL_MAX_EVENTS = 16; // The array of pending epoll events and the index of the next event to be handled. struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS]; if (argc < 2) { printf("Usage: %s <file1> [file2] [file3] ...\n", argv[0]); return -1; } /* epoll_create */ mEpollFd = epoll_create(8); /* for each file: * open it * add it to epoll: epoll_ctl(...EPOLL_CTL_ADD...) */ for (i = 1; i < argc; i++) { //int tmpFd = open(argv[i], O_RDONLY|O_NONBLOCK); int tmpFd = open(argv[i], O_RDWR); add_to_epoll(tmpFd, mEpollFd); } /* epoll_wait */ while (1) { int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, -1); for (i = 0; i < pollResult; i++) { printf("Reason: 0x%x\n", mPendingEventItems[i].events); int len = read(mPendingEventItems[i].data.fd, buf, DATA_MAX_LEN); buf[len] = '\0'; printf("get data: %s\n", buf); //sleep(3); } } return 0;}
测试方法
$ gcc -o -epoll epoll.c$ mkfifo tmp/2$ ./epoll tmp /1 tmp/2 tmp/3$ echo aa>tmp/1$ echo bb>tmp/2
储备知识二(通信)
首先思考一个问题,如果让你自己设计一个输入系统会怎么做呢?
- 首先创建一个进程,用于读取分发那些事件
- 写出应用程序,用于处理应用事件,并对进程状态做出回馈
@(可以看出读取进程和APP进程之间是需要双向通信)
此时我们知道Binder系统在Android中承担大部分的进程通信角色
但是在输入系统中,是不是照样适用呢?答案是否定的。
因为Binder是单向通信,Clinet发出指令,Server接收指令,Server不能主动
如果需要双向通信,Binder的任何一端口都是Server+Client,这样会使得系统很复杂。所以输入系统中用的是SocketPair
缺点:(只适用如下情况:)
- 适用于线程通信
- 具有亲缘关系的进程间通信
如果我们要在两个APP之间进行通讯,那就必须把其中的一个文件句柄比如fd2通过Binder传递给另一个APP
使用socketpair函数流程
@(参考frameworks\native\libs\input\InputTransport.cpp)
- pthread_create—创建线程1
- read—读数据: 线程1发出的数据
- sprintf—main thread向thread1 发出: Hello, thread1
- 子线程sprintf— 向 main线程发出: Hello, main thread
- read—读取数据(main线程发回的数据)
#include <pthread.h>#include <unistd.h>#include <stdio.h>#include <sys/types.h> #include <sys/socket.h>#define SOCKET_BUFFER_SIZE (32768U)void *function_thread1 (void *arg){ int fd = (int)arg; char buf[500]; int len; int cnt = 0; while (1) { /* 向 main线程发出: Hello, main thread */ len = sprintf(buf, "Hello, main thread, cnt = %d", cnt++); write(fd, buf, len); /* 读取数据(main线程发回的数据) */ len = read(fd, buf, 500); buf[len] = '\0'; printf("%s\n", buf); sleep(5); } return NULL;}int main(int argc, char **argv){ int sockets[2]; socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets); int bufferSize = SOCKET_BUFFER_SIZE; setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); /* 创建线程1 */ pthread_t threadID; pthread_create(&threadID, NULL, function_thread1, (void *)sockets[1]); char buf[500]; int len; int cnt = 0; int fd = sockets[0]; while(1) { /* 读数据: 线程1发出的数据 */ len = read(fd, buf, 500); buf[len] = '\0'; printf("%s\n", buf); /* main thread向thread1 发出: Hello, thread1 */ len = sprintf(buf, "Hello, thread1, cnt = %d", cnt++); write(fd, buf, len); }}
测试方法
gcc -o socketpair socketpari.c -lpthread./socketpari
既然我们上面说了,socketpair不能在不同APP之间通信,那么怎么做才达到在不同APP之间通信的目的呢?
@(要明白这个道理,就必须了解文件句柄与进程和线程的关系)
我们知道task_struct表示进程结构体,在进程结构体中有struct files_struct * files;
struct files_struct中有struct file __ruc **fd;数组这个数组的下标就是对应的fd。
同样对于进程2也会有一个数组
此时如果我要把APP1进程里面的fd传递给APP2,此时就会先在APP2的进程的file__ruc数组中找出一个空项,然后让这个空项指向APP1对应的file__ruc[X]
使用binder传输file\__ruc[X]
- Android输入系统(一)
- Android 输入系统(一)InputManagerService
- Android触摸屏输入系统
- Android触摸屏输入系统
- Android 输入系统
- Android触摸屏输入系统
- Android触摸屏输入系统
- Android触摸屏输入系统
- Android 输入系统
- Android输入系统
- Android输入系统
- ANDROID输入系统
- android 输入系统
- android用户输入系统
- 【读书笔记】Android 输入系统
- Android:输入系统(InputChannel)
- Android输入系统(二)
- Android输入系统
- .Net程序员学用Oracle系列(14):子查询、集合查询
- mysql中一些常用操作
- 用cxf做基于spring的web service开发(八)
- 算法训练 排序
- 机器学习&深度学习资料分享
- Android输入系统(一)
- LeakCanary——直白的展现Android中的内存泄露
- IDEA 2016 注册码
- TLD matlab c++混编代码的运行配置方法
- ViewPager广告位_完整版_轮播_手势(指)控制
- Hamming Distance
- Layout inflate遇到的坑
- 输入url到页面返回的全过程
- Android -- Android Init进程的处理流程分析