代码示例
代码运行环境:Android , 开发工具AS,用AS创建一个JIN工程,然后copy示例代码覆盖工程的natvie-lib.cpp文件即可
代码逻辑说明:创建一个首次超时时间2S,后续每间隔50毫秒超时的定时器文件符实例,超时20次后停止
#include <jni.h>
#include <string>
#include <thread>
#include <unistd.h>
#include <sys/epoll.h>
#include <android/log.h>
#include <sys/eventfd.h>
#include <sys/timerfd.h>
using namespace std;
static char *TAG = "JNI-DEMO";
static static int MAX_EVENTS_SIZE = 1024;
void demo_timerfd() {
int timerFd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (timerFd == -1) {
__android_log_print(ANDROID_LOG_WARN, TAG, "timerfd_create fail");
return;
}
int epollFd = epoll_create(MAX_EVENTS_SIZE);
if (epollFd == -1) {
__android_log_print(ANDROID_LOG_WARN, TAG, "epoll_create fail");
return;
}
struct epoll_event ev;
ev.data.fd = timerFd;
ev.events = EPOLLIN;
int ret = epoll_ctl(epollFd, EPOLL_CTL_ADD, timerFd, &ev);
if (ret == -1) {
__android_log_print(ANDROID_LOG_WARN, TAG, "epoll_ctl fail");
}
struct itimerspec spec;
//定时器周期超时时间
spec.it_interval.tv_sec = 0;
//nsec是纳秒,每50ms超时
spec.it_interval.tv_nsec = 50 * 1000 * 1000; // 1ms = 1000000ns
//定时器首次超时时间,2S后
spec.it_value.tv_sec = 2;
spec.it_value.tv_nsec = 0;
//启动定时间
if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
__android_log_print(ANDROID_LOG_WARN, TAG, "start timer fail");
return;
}
__android_log_print(ANDROID_LOG_INFO, TAG, "start timer");
thread threadEpoll([epollFd, timerFd] {
struct epoll_event evs[MAX_EVENTS_SIZE];
int timeOutCount = 0;
while (true) {
int count = epoll_wait(epollFd, evs, MAX_EVENTS_SIZE, -1);
__android_log_print(ANDROID_LOG_INFO, TAG, "epoll_wait, count:%d", count);
for (int i = 0; i < count; i++) {
if (evs[i].data.fd == timerFd && evs[i].events && EPOLLIN) {
uint64_t value = 0;
int ret = 0;
//!!!注意需要作读取的操作,否侧会不停的触发超时/可读事件,只有做了读取的操作才算消费了超时事件
ret = read(timerFd, &value, sizeof(uint64_t));
__android_log_print(ANDROID_LOG_INFO, TAG, "value:%u, ret:%d", value, ret);
timeOutCount++;
if (timeOutCount == 19) {
struct itimerspec spec;
spec.it_interval.tv_sec = 0;
spec.it_interval.tv_nsec = 0;
spec.it_value.tv_sec = 0;
spec.it_value.tv_nsec = 0;
if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
__android_log_print(ANDROID_LOG_WARN, TAG, "stop timer fail");
return;
}
__android_log_print(ANDROID_LOG_INFO, TAG, "stop timer, timeOutCount:%d", timeOutCount);
}
}
}
}
});
threadEpoll.detach();
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
demo_timerfd();
return env->NewStringUTF(hello.c_str());
}
运行效果
使用注意点
epoll_wait到超时事件后,需要从timefd读数据,否侧可读/超时事件会一直触发!!!!
如上示例代码如果如下读操作的代码删除,会发现定时器似乎不按指定的超时参数工作,一下就epoll_wait到了20次,然后很快执行到了停止定时间的代码
ret = read(timerFd, &value, sizeof(uint64_t));
运行结果如下:
timerfd的说明
timerfd简单的一句话描述就是定时器于文件描述符的形式去使用,即定时器触发超时时会住其绑定的文件描述符写数据,然后我们就可以从文件描述读取出数据,就是专门提供给epoll编程模式下来使用了定时器了
相关的接口描述更是简结明了:通过文件描述符通知超时事件的定时器!
eventfd的头文件
#include <sys/timerfd.h>
主要是创建timerfd实例,以及设备定时器的启动参数,获取定时器当前的配置参数等
#if __ANDROID_API__ >= 19
int timerfd_create(clockid_t __clock, int __flags) __INTRODUCED_IN(19);
#endif /* __ANDROID_API__ >= 19 */
/** The timerfd_settime() flag to use absolute rather than relative times. */
#define TFD_TIMER_ABSTIME (1 << 0)
/** The timerfd_settime() flag to cancel an absolute timer if the realtime clock changes. */
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
/**
* [timerfd_settime(2)](http://man7.org/linux/man-pages/man2/timerfd_settime.2.html) starts or
* stops a timer.
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*
* Available since API level 19.
*/
#if __ANDROID_API__ >= 19
int timerfd_settime(int __fd, int __flags, const struct itimerspec* __new_value, struct itimerspec* __old_value) __INTRODUCED_IN(19);
/**
* [timerfd_gettime(2)](http://man7.org/linux/man-pages/man2/timerfd_gettime.2.html) queries the
* current timer settings.
*
* Returns 0 on success, and returns -1 and sets `errno` on failure.
*
* Available since API level 19.
*/
int timerfd_gettime(int __fd, struct itimerspec* __current_value) __INTRODUCED_IN(19);
#endif /* __ANDROID_API__ >= 19 */
启动与停止定时器
通过timerfd_settime接口启动与停止定时器,设备所有时间参数为零,即可暂停定时器。注意时间参数是秒 + 纳秒,即定时间的精度可以到纳秒!
struct itimerspec spec;
//定时器周期超时时间
spec.it_interval.tv_sec = 0;
//nsec是纳秒,每50ms超时
spec.it_interval.tv_nsec = 50 * 1000 * 1000; // 1ms = 1000000ns
//定时器首次超时时间,2S后
spec.it_value.tv_sec = 2;
spec.it_value.tv_nsec = 0;
//启动定时器
if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
__android_log_print(ANDROID_LOG_WARN, TAG, "start timer fail");
return;
}
//暂停定时器
struct itimerspec spec;
spec.it_interval.tv_sec = 0;
spec.it_interval.tv_nsec = 0;
spec.it_value.tv_sec = 0;
spec.it_value.tv_nsec = 0;
if (timerfd_settime(timerFd, 0, &spec, nullptr) == -1) {
__android_log_print(ANDROID_LOG_WARN, TAG, "stop timer fail");
return;
}