編輯:關於Android編程
因此帶來CPU和內存的雙重開銷、但達到單機10G+ cache以及QPS上萬之後這些開銷就會給雙方相對帶來一些
細微性能差別。
基本的數據結構:@ae.h
//定義文件事件處理接口(函數指針)
typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask); //時間事件處理接口(函數指針),該函數返回定時的時長 typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData); typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData); //aeMain中使用,在調用處理事件前調用 typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);
//文件事件結構體 typedef struct aeFileEvent { //讀或者寫,也用於標識該事件結構體是否正在使用 int mask; /* one of AE_(READABLE|WRITABLE) */ //讀事件的處理函數 aeFileProc *rfileProc; //寫事件的處理函數 aeFileProc *wfileProc; //傳遞給上述兩個函數的數據 void *clientData; } aeFileEvent; //時間事件 typedef struct aeTimeEvent { //時間事件標識符,用於唯一標識該時間事件,並且用於刪除時間事件 long long id; /* time event identifier. */ long when_sec; /* seconds */ long when_ms; /* milliseconds */ //該事件對應的處理程序 aeTimeProc *timeProc; //時間事件的最後一次處理程序,若已設置,則刪除時間事件時會被調用 aeEventFinalizerProc *finalizerProc; void *clientData; struct aeTimeEvent *next; } aeTimeEvent; //這裡用於保存已觸發的事件 typedef struct aeFiredEvent { int fd; int mask; } aeFiredEvent;
/* State of an event based program */ typedef struct aeEventLoop { //最大文件描述符的值 int maxfd; /* highest file descriptor currently registered */ //文件描述符的最大監聽數 int setsize; /* max number of file descriptors tracked */ //用於生成時間事件的唯一標識id long long timeEventNextId; //用於檢測系統時間是否變更(判斷標准 now
typedef struct aeApiState { int epfd; struct epoll_event *events; } aeApiState;
//ae底層的數據創建以及初始化 static int aeApiCreate(aeEventLoop *eventLoop) { aeApiState *state = zmalloc(sizeof(aeApiState)); if (!state) return -1; //創建setsize個epoll_event state->events = zmalloc(sizeof(struct epoll_event)*eventLoop->setsize); if (!state->events) { zfree(state); return -1; } state->epfd = epoll_create(1024); /* 1024 is just a hint for the kernel */ if (state->epfd == -1) { zfree(state->events); zfree(state); return -1; } eventLoop->apidata = state; return 0; } //創建事件循環,setsize為最大事件的的個數,對於epoll來說也是epoll_event的個數 aeEventLoop *aeCreateEventLoop(int setsize) { aeEventLoop *eventLoop; int i; //分配該結構體的內存空間 if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err; eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize); eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize); if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err; //初始化最多setsize個事件 eventLoop->setsize = setsize; eventLoop->lastTime = time(NULL); eventLoop->timeEventHead = NULL; eventLoop->timeEventNextId = 0; eventLoop->stop = 0; eventLoop->maxfd = -1; eventLoop->beforesleep = NULL; //這一步為創建底層IO處理的數據,如epoll,創建epoll_event,和epfd if (aeApiCreate(eventLoop) == -1) goto err; /* Events with mask == AE_NONE are not set. So let's initialize the * vector with it. */ for (i = 0; i < setsize; i++) eventLoop->events[i].mask = AE_NONE; return eventLoop; err: if (eventLoop) { zfree(eventLoop->events); zfree(eventLoop->fired); zfree(eventLoop); } return NULL; }
2、aeCreateFileEvent
對於創建文件事件,需要傳入一個該事件對應的處理程序,當事件發生時,會調用對應的回調函數。
這裡設計的aeFileEvent結構體就是將事件源(FD),事件,事件處理程序關聯起來。
//添加監聽的事件,其中如果該fd對應的事件已經存在,則為修改合並舊的事件 static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { aeApiState *state = eventLoop->apidata; struct epoll_event ee = {0}; /* avoid valgrind warning */ /* If the fd was already monitored for some event, we need a MOD * operation. Otherwise we need an ADD operation. */ //判斷fd是否已經添加了事件的監聽 int op = eventLoop->events[fd].mask == AE_NONE ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; ee.events = 0; mask |= eventLoop->events[fd].mask; /* Merge old events */ if (mask & AE_READABLE) ee.events |= EPOLLIN; if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; ee.data.fd = fd; if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1; return 0; } //刪除指定事件的監聽 static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int delmask) { aeApiState *state = eventLoop->apidata; struct epoll_event ee = {0}; /* avoid valgrind warning */ int mask = eventLoop->events[fd].mask & (~delmask); ee.events = 0; if (mask & AE_READABLE) ee.events |= EPOLLIN; if (mask & AE_WRITABLE) ee.events |= EPOLLOUT; ee.data.fd = fd; if (mask != AE_NONE) { epoll_ctl(state->epfd,EPOLL_CTL_MOD,fd,&ee); } else { /* Note, Kernel < 2.6.9 requires a non null event pointer even for * EPOLL_CTL_DEL. */ epoll_ctl(state->epfd,EPOLL_CTL_DEL,fd,&ee); } } //創建文件事件,並將該事件注冊到eventLoop中 int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData) { if (fd >= eventLoop->setsize) { errno = ERANGE; return AE_ERR; } //直接使用fd來獲取FileEvent,來後面分離事件時也采用這種方法(直接索引) aeFileEvent *fe = &eventLoop->events[fd]; //該該事件添加eventLoop中或者修改原來的已有的(保留舊的) if (aeApiAddEvent(eventLoop, fd, mask) == -1) return AE_ERR; fe->mask |= mask; //將該事件的處理程序放到對應的位置 if (mask & AE_READABLE) fe->rfileProc = proc; if (mask & AE_WRITABLE) fe->wfileProc = proc; //設置將要傳遞給該事件處理程序的數據 fe->clientData = clientData; if (fd > eventLoop->maxfd) eventLoop->maxfd = fd; return AE_OK; }
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { aeApiState *state = eventLoop->apidata; int retval, numevents = 0; //等待事件產生 retval = epoll_wait(state->epfd,state->events,eventLoop->setsize, tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1); if (retval > 0) { int j; numevents = retval; for (j = 0; j < numevents; j++) { int mask = 0; struct epoll_event *e = state->events+j; if (e->events & EPOLLIN) mask |= AE_READABLE; if (e->events & EPOLLOUT) mask |= AE_WRITABLE; if (e->events & EPOLLERR) mask |= AE_WRITABLE; if (e->events & EPOLLHUP) mask |= AE_WRITABLE; // 利用fired數組記錄觸發的事件 eventLoop->fired[j].fd = e->data.fd; eventLoop->fired[j].mask = mask; } } return numevents; } //事件處理程序 int aeProcessEvents(aeEventLoop *eventLoop, int flags) { int processed = 0, numevents; //若什麼都沒有設置,則直接返回 /* Nothing to do? return ASAP */ if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0; //如果有文件事件或者設置了時間事件並且沒有設置DONT_WAIT標志 /* Note that we want call select() even if there are no * file events to process as long as we want to process time * events, in order to sleep until the next time event is ready * to fire. */ if (eventLoop->maxfd != -1 || ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) { int j; aeTimeEvent *shortest = NULL; struct timeval tv, *tvp; if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) //查找時間最早的時間事件 shortest = aeSearchNearestTimer(eventLoop); if (shortest) { long now_sec, now_ms; aeGetTime(&now_sec, &now_ms); tvp = &tv; /* How many milliseconds we need to wait for the next * time event to fire? */ long long ms = (shortest->when_sec - now_sec)*1000 + shortest->when_ms - now_ms; // 找到最早的時間事件與當前時間差值就是epoll wait時間 if (ms > 0) { tvp->tv_sec = ms/1000; tvp->tv_usec = (ms % 1000)*1000; } else { tvp->tv_sec = 0; tvp->tv_usec = 0; } } else { /* If we have to check for events but need to return * ASAP because of AE_DONT_WAIT we need to set the timeout * to zero */ if (flags & AE_DONT_WAIT) { tv.tv_sec = tv.tv_usec = 0; tvp = &tv; } else { //如果沒有時間事件則可以阻塞、如果此時加入一個Timer event,啥時候喚醒呢?! /* Otherwise we can block */ tvp = NULL; /* wait forever */ } } numevents = aeApiPoll(eventLoop, tvp); for (j = 0; j < numevents; j++) { aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd]; int mask = eventLoop->fired[j].mask; int fd = eventLoop->fired[j].fd; int rfired = 0; /* note the fe->mask & mask & ... code: maybe an already processed * event removed an element that fired and we still didn't * processed, so we check if the event is still valid. */ if (fe->mask & mask & AE_READABLE) { rfired = 1; fe->rfileProc(eventLoop,fd,fe->clientData,mask); } if (fe->mask & mask & AE_WRITABLE) { //這裡的判斷是為了防止重復調用 if (!rfired || fe->wfileProc != fe->rfileProc) fe->wfileProc(eventLoop,fd,fe->clientData,mask); } processed++; } } /* Check time events */ if (flags & AE_TIME_EVENTS) processed += processTimeEvents(eventLoop); return processed; /* return the number of processed file/time events */ }
通知的使用,無疑是Android系統的亮點之一;就連IOS在5.0開始也引入了類似通知的技巧。可見它的實用性。今天這個小案例,就學習一下通知的基本使用,API是使用最新的
老早就使用了,但是現在才寫,惰性太大,現在改現在做產品的話相信大家基本都做分享吧,一個是項目的需求需要,還有一個是可以很好的宣傳自己的產品,其他的好處根據情況而論其實每個
0、基礎回顧PropertyAnimation,屬性動畫,顧名思義就是利用對象的屬性變化形成動畫的效果。屬性動畫的類可以用Animator這個抽象類來表示,通常使用它的子
上一篇,初步開發了這個應用,功能都有了(見http://www.jb51.net/article/96992.htm 點擊打開鏈接)。但是遺留了兩個問題:1、還是無法卸載