Android Input 学习-INotify与Epoll机制

2018-08-03  本文已影响0人  伪乐观的W先生

使用背景

Android Input需要使用InputReader去监控设备节点的一些动作,包括节点的新建和删除动作以及如何去确定节点中是否有内容可以去读.最简单的方法是起一个线程在循环中不断地去做轮询(polling),但是这样做的效率比较低,而且会导致设备的电量在无意义的轮询中消耗掉.众所周知Android使用的Linux内核,因此面对这种问题,Android使用了Linux提供的INotify和Epoll机制,异步的去读取消息,而不是一直在轮询.

1. INotify机制

int inotifyfd = inotify_init();
int wd = inotify_add_watch(inotifyfd, "/dev/input",  IN_CREATE | IN_DELETE);

以上代码完成后,当/dev/input下的设备节点发生创建与删除操作的时候,会将相应的信息写入到inotifyfd所描述的inotify对象中,此时就可以通过read()函数从inotifyfd描述符中将事件信息读取出来.
事件信息使用的结构体inotify_event 如下:

struct inotify_event {
            __s32        wd; /*watch descriptor,事件对应watch对象的描述符*/
            __u32        mask; /*watch mask,事件类型,就是我们需要监听的,例如文件创建的话,此时的mask就是IN_CREATE*/
            __u32        cookie; /*cookie to sychronize two events*/
            __u32        len; /*length (including nulls) of name,name字段的长度*/
            char         name[0]; /*stub for possibale name,name字段的长度是0,也就是说是可变长的,用于存储产生此事件的文件路径*/
};

当监听事件发生事件,可以通过read()函数将一个或多个未读取的事件信息读取出来:

struct inotify_event *event;
char event_buf[512];
event = read(inotifyfd, event_buf, sizeof(event_buf));

能够读取的事件数量取决于数组的长度,成功读取事件信息后,便可以根据inotify_event结构体的字段判断事件类型,以及产生事件的文件路径.

//demo 代码
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

int watch_inotify_events(int fd) {
    char event_buf[512];
    int ret;
    int event_pos = 0;
    int event_size = 0;
    struct inotify_event *event;
    /*读事件是否发生,没有发生则会阻塞*/
    ret = read(fd, event_buf, sizeof(event_buf));
    /*若read的返回值,小于inotify_event大小,说明我们连最基本的一个event都读取出来,是错误的结果*/
    if(ret < (int)sizeof(struct inotify_event)) {
        printf("read error,could get event");
        return -1;
    }
    /*一次读取可能会去读取多个事件,需要一个循环全部读取出来*/
    while(ret > (int)sizeof(struct inotify_event)) {
        event = (struct inotify_event*)(event_buf + event_pos);
        if(event->len) {
            if(event->mask & IN_CREATE) {
                printf("create file:%s successfully \n", event->name);
            } else {
                printf("delete file:%s successfully \n", event->name);
            }
        }
        //event 的真实大小,name是可变的,所以要加上event->len
        event_size = sizeof(struct inotify_event) + event->len;
        ret -= event_size;
        event_pos += event_size;
    }
    return 0;
}
int main(int argc, char** argv) {
    int inotifyFd;
    int ret;
    if (argc != 2) {
        printf("useage: %s <dir> \n", argv[0]);
    }

    /*inotify初始化*/
    inotifyFd = inotify_init();
    if(inotifyFd == -1) {
        printf("inotify_init error!\n");
        return -1;
    }
    ret = inotify_add_watch(inotifyFd, argv[1], IN_CREATE | IN_DELETE) ;
    watch_inotify_events(inotifyFd);
    if(inotify_rm_watch(inotifyFd, ret) == -1) {
        printf("notify_rm_watch error!\n");
        return -1;
    }
    //关闭描述符
    close(inotifyFd);
    return 0;
}

2.Epoll机制

//完整demo代码
#include <sys/inotify.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

int watch_inotify_events(int fd) {
    char event_buf[512];
    int ret;
    int event_pos = 0;
    int event_size = 0;
    struct inotify_event *event;
    /*读事件是否发生,没有发生则会阻塞*/
    ret = read(fd, event_buf, sizeof(event_buf));
    /*若read的返回值,小于inotify_event大小,说明我们连最基本的一个event都读取出来,是错误的结果*/
    if(ret < (int)sizeof(struct inotify_event)) {
        printf("read error,could get event");
        return -1;
    }
    /*一次读取可能会去读取多个事件,需要一个循环全部读取出来*/
    while(ret > (int)sizeof(struct inotify_event)) {
        event = (struct inotify_event*)(event_buf + event_pos);
        if(event->len) {
            if(event->mask & IN_CREATE) {
                printf("create file:%s successfully \n", event->name);
            } else {
                printf("delete file:%s successfully \n", event->name);
            }
        }
        //event 的真实大小,name是可变的,所以要加上event->len
        event_size = sizeof(struct inotify_event) + event->len;
        ret -= event_size;
        event_pos += event_size;
    }
    return 0;
}


int main(int argc, char** argv) {
    int epollFd;
    int inotifyFd;
    int pendingEventCount;
    int pendingEventIndex;
    epollFd = epoll_create(8);
    inotifyFd = inotify_init();
    int result_notify = inotify_add_watch(inotifyFd, argv[1], IN_CREATE | IN_DELETE);
    if(result_notify < 0) {
        printf("Could not register INotify. \n");
        return -1;
    }
    if(epollFd < 0) {
        printf("Could not create epoll instance. \n");
        return -1;
    }
    struct epoll_event eventItem;
    eventItem.events = EPOLLIN;
    int result_epoll = epoll_ctl(epollFd, EPOLL_CTL_ADD, inotifyFd, &eventItem);
    if(result_epoll != 0) {
        printf("Could not add INotify to epoll instance.  \n");
    }
    struct epoll_event pendingEventItems[16];
    int pollResult = epoll_wait(epollFd, pendingEventItems, 16, 30*1000ll);
    if(pollResult > 0) {
        pendingEventCount = size_t(pollResult);
    } else {
        pendingEventCount = 0;
    }
    while(pendingEventIndex < pendingEventCount) {
        const struct epoll_event& eventItem = pendingEventItems[pendingEventIndex++];
        if (eventItem.events & EPOLLIN) {
            watch_inotify_events(inotifyFd);
        }
    }
    return 0;
}
123@123-good-man:./a.out ~/Documents/cpptest/ &

//运行结果
123@123-good-man:~/Documents/cpptest$ touch 201808.txt
create file:201808.txt successfully 
[1]+  Done                    ./a.out ~/Documents/cpptest/
123@123-good-man:~/Documents/cpptest$ 

参考:《深入理解Android卷|||》 第五章 深入理解Android输入系统

上一篇下一篇

猜你喜欢

热点阅读