最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android Handler中的休眠喚醒實現(xiàn)詳解

 更新時間:2023年01月13日 14:26:03   作者:android_greenhand  
這篇文章主要為大家介紹了Android Handler中的休眠喚醒實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

Handler中的奇奇怪怪

了解Handler原理時,有一個疑問handler中的休眠/喚醒不用Java中wait和notify呢,而是調(diào)用native方法(nativePollOnce/nativeWake)呢,奇奇怪怪 的又得需要學(xué)起來,沒事找事的一天嘛。

這樣不行嗎?wait/notify 偽代碼

MessageQueue.java
//調(diào)用MessageQueue的next方法獲取消息
Message next() {
      、、、
      synchronized (this) {
          //這時候檢查隊列有沒有消息,沒有消息調(diào)用this.wait()等待  
          if (message == null) {
              this.wait();
          }
          if(有消息但消息未到期){
            this.wait(time);
          }
      }
      、、、
  }
//調(diào)用MessageQueue.enqueueMessage()添加消息
enqueueMessage(Message message) {
    、、、
    synchronized (this) {
        //消息加入隊列后會調(diào)用this.notity()喚醒next()方法
        if (message != null) {
            this.notify();
        }
    }
      、、、
  }

在學(xué)習(xí)nativePollOnce/nativeWake前,還需要對Linux相關(guān)的知識熟悉一下。

Linux相關(guān)

eventfd

eventfd 是從內(nèi)核2.6.22開始支持的一種新的事件等待/通知機(jī)制。用來通知事件的文件描述符,它不僅可以用于進(jìn)程間的通信,還可以用戶內(nèi)核發(fā)信號給用戶層的進(jìn)程。簡而言之:eventfd 就是用來觸發(fā)事件通知,它只有一個創(chuàng)建方法:

int eventfd(unsigned int initval, int flags); 表示創(chuàng)建一個 eventfd 文件并返回文件描述符

參數(shù):initval, 初始值

參數(shù):flags

  • EFD_CLOEXEC 會自動關(guān)閉這個文件描述符。
  • EFD_NONBLOCK 執(zhí)行 read / write 操作時,不會阻塞。
  • EFD_SEMAPHORE count 遞減 1。

相關(guān)操作

  • write(): 其實是執(zhí)行 add 操作,累加 count值。
  • read(): 根據(jù)設(shè)置不同的flags標(biāo)記,讀取到不同的值

EFD_SEMAPHORE:讀到的值為 1,同時 count 值遞減 1。
其他的都是:讀取 count 值后置 0

阿西吧什么亂七八糟的,別急看看這個下面這個Demo;

eventfd demo

  #include <cstdlib>
  #include <inttypes.h>
  #include <iostream>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string>
  #include <sys/eventfd.h>
  #include <unistd.h>
  using namespace std;
  int main(int argc, char* argv[]) {
      int event_fd;
      if (argc < 2) {
          std::cout << "please input llegal argv " << endl;
          exit(EXIT_FAILURE);
      }
      event_fd = eventfd(0, EFD_NONBLOCK);
      if (event_fd == -1) {
          std::cout << "create evebtFd fail" << endl;
          exit(EXIT_FAILURE);
      }
      switch (fork()) {
      case 0:
          for (int j = 1; j < argc; j++) {
              long u = atoi(argv[j]);
              printf("Child writing %lu to efd\n", u);
              write(event_fd, &u, sizeof(long));
          }
          printf("Child completed write loop\n");
          exit(EXIT_SUCCESS);
      default:
          sleep(2);
          long u;
          printf("Parent about to read\n");
          read(event_fd, &u, sizeof(long));
          printf("Parents first read %lu from efd\n", u);
          long u2;
          read(event_fd, &u2, sizeof(long));
          printf("Parents second read %lu from efd\n", u2);
          exit(EXIT_SUCCESS);
      }
  }

?? #include <sys/eventfd.h> 是在Linux操作系統(tǒng)中的,在Mac電腦中是找不到包的,需要裝虛擬機(jī)或者其他的C++開發(fā)軟件包,這里推薦一個在線免費的編譯C++的軟件Lightly

Q eventfd和socket、pipe、fd_set、有什么區(qū)別和聯(lián)系?

Epoll

epoll是Linux內(nèi)核為處理大批量文件描述符而改進(jìn)的poll,是Linux下多路復(fù)用IO接口select/poll的增強(qiáng)版本,它能顯著提高程序在大量并發(fā)連接中,只有少量活躍的情況下的系統(tǒng)CPU利用率。另一點原因就是獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內(nèi)核IO事件異步喚醒而加入Ready隊列的描述符集合就行了。提高應(yīng)用程序效率。 來源自百度百科

epoll API

int epoll_create(int size)

創(chuàng)建 eventpoll 對象,返回一個 epfd,即 eventpoll 句柄。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

對eventpoll執(zhí)行的操作,返回值:成功 0;失敗 -1

epfd 對一個 eventPoll 進(jìn)行操作

op 表示要執(zhí)行的操作,包括 EPOLL_CTL_ADD (添加)、EPOLL_CTL_DEL (刪除)、EPOLL_CTL_MOD (修改);

fd 表示被監(jiān)聽的文件描述符;

event 表示要被監(jiān)聽的事件,包括:

  • EPOLLIN(表示被監(jiān)聽的fd有可以讀的數(shù)據(jù))
  • EPOLLOUT(表示被監(jiān)聽的fd有可以寫的數(shù)據(jù))
  • EPOLLPRI(表示有可讀的緊急數(shù)據(jù))
  • EPOLLERR(對應(yīng)的fd發(fā)生異常)
  • EPOLLHUP(對應(yīng)的fd被掛斷)
  • EPOLLET(設(shè)置EPOLL為邊緣觸發(fā))
  • EPOLLONESHOT(只監(jiān)聽一次)

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)

返回值:監(jiān)聽到的產(chǎn)生的事件數(shù) 等待 epfd 監(jiān)聽的 fd 所產(chǎn)生對應(yīng)的事件。

  • epfd 表示 eventpoll句柄;
  • events 表示回傳處理事件的數(shù)組;
  • maxevents 表示每次能處理的最大事件數(shù);
  • timeout:等待 IO 的超時時間,-1 表示一直阻塞直到來 IO 被喚醒,大于 0 表示阻塞指定的時間后被喚醒

epoll 使用示例

創(chuàng)建一個管道,使用 epoll 監(jiān)聽管道讀端,然后進(jìn)入阻塞:

    #include <iostream>
    #include <stdio.h>
    #include <string>
    #include <sys/epoll.h>
    #include <sys/eventfd.h>
    #include <unistd.h>
    using namespace std;
    int main(int argc, char* argv[]) {
        if (argc < 2) {
            exit(EXIT_FAILURE);
        }
        int event_fd;
        int epoll_fd;
        event_fd = eventfd(0, EFD_NONBLOCK);
        if (event_fd == -1) {
            std::cout << "create evebtFd fail";
            exit(EXIT_FAILURE);
        }
        epoll_fd = epoll_create(8);
        if (epoll_fd < 0) {
            std::cout << "create epollFd  fail";
        }
        struct epoll_event read_event;
        read_event.events = EPOLLIN;
        epoll_ctl(epoll_fd, EPOLL_CTL_ADD, event_fd, &read_event);
        switch (fork()) {
        case 0:
            for (int j = 1; j < argc; j++) {
                long u = atoi(argv[j]);
                sleep(1);
                printf("Child writing %lu to efd\n", u);
                write(event_fd, &u, sizeof(long));
            }
            printf("Child completed write loop\n");
            exit(EXIT_SUCCESS);
        default:
            printf("Parent about to read\n");
            struct epoll_event events[16];
            int ret;
            while (1) {
                ret = epoll_wait(epoll_fd, events, 1, -1);
                printf("Parent  epoll_wait return ret : %d\n", ret);
                if (ret > 0) {
                    long u;
                    read(event_fd, &u, sizeof(long));
                    printf("Parents  read %lu from efd\n", u);
                }
            }
            exit(EXIT_SUCCESS);
        }
    }

結(jié)果

Handler 中的 epoll 源碼分析

主要分析 MessageQueue.java 中的三個 native 函數(shù):

     private native static long nativeInit(); //初始化
     private native void nativePollOnce(long ptr, int timeoutMillis); //阻塞
     private native static void nativeWake(long ptr); //喚醒

「nativeInit 返回long,這是為什么?」 預(yù)知一下,或許這個可以解答我們的問題

nativeInit

首先來看 nativeInit 方法,nativeInit 在 MessageQueue 構(gòu)造函數(shù)中被調(diào)用,其返回了一個底層對象的指針:

      MessageQueue(boolean quitAllowed) {
            mQuitAllowed = quitAllowed;
            mPtr = nativeInit();   //保存NativeMessageQueue
        }
      //android_os_MessageQueue.cpp
      static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
        NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
        ...
        return reinterpret_cast<jlong>(nativeMessageQueue);
      }

einterpret_cast<type-id> (expression)

type-id 必須是一個指針、引用、算術(shù)類型、函數(shù)指針或者成員指針。它可以把一個指針轉(zhuǎn)換成一個整數(shù),也可以把一個整數(shù)轉(zhuǎn)換成一個指針(先把一個指針轉(zhuǎn)換成一個整數(shù),再把該整數(shù)轉(zhuǎn)換成原類型的指針,還可以得到原先的指針值

返回值是NativeMessageQueue,而 NativeMessageQueue 初始化時會創(chuàng)建一個底層的 Looper 對象:

  NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false); 
        Looper::setForThread(mLooper);
    }
  }

Looper 的構(gòu)造函數(shù)如下:

  Looper::Looper(bool allowNonCallbacks) :
        mAllowNonCallbacks(allowNonCallbacks), ...{
    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    ...
    rebuildEpollLocked();
  }

有沒有熟悉的感覺,這和我們的Epoll的demo很相似,首先通過創(chuàng)建eventFd, ,專門用于事件通知。接著來看 rebuildEpollLocked 方法:

  void Looper::rebuildEpollLocked() {
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    struct epoll_event eventItem;
    memset(& eventItem, 0, sizeof(epoll_event));
    eventItem.events = EPOLLIN;
    eventItem.data.fd = mWakeEventFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem); 
    ...
  }

可以看到我們已經(jīng)熟悉的 epoll 操作了:通過 epoll_create 創(chuàng)建 epoll 對象,然后調(diào)用 epoll_ctl 添加 mWakeEventFd 為要監(jiān)聽的文件描述符。

nativePollOnce

之前學(xué)習(xí) Handler 機(jī)制時多次看到過 nativePollOnce 方法,也知道它會進(jìn)入休眠,下面就具體看看它的原理。對應(yīng)的底層調(diào)用同樣是在 android_os_MessageQueue.cpp 中:

    static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
          jlong ptr, jint timeoutMillis) {
      NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
      nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
    }
    void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
      mLooper->pollOnce(timeoutMillis);
      ...
    }

可以看到實現(xiàn)同樣是在 Looper.cpp 中,接著來看 Looper 的 pollOnce 方法:

     int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
       for (;;) {
           ...
           result = pollInner(timeoutMillis);
       }
     }
     int Looper::pollInner(int timeoutMillis) {
       ...
       struct epoll_event eventItems[EPOLL_MAX_EVENTS];
       int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
       ...

至此通過調(diào)用 epoll_wait 方法,當(dāng)前線程進(jìn)入休眠,等待被喚醒。

nativeWake

最后來看如何通過 nativeWake 喚醒線程,首先是 android_os_MessageQueue.cpp 中:

  static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
  }
  void NativeMessageQueue::wake() {
    mLooper->wake();
  }

與 nativeInit、nativePollOnce 一樣,最終實現(xiàn)都是在 Looper.cpp 中,Looper 的 wake 方法如下:

void Looper::wake() {
  uint64_t inc = 1;
  ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
  if (nWrite != sizeof(uint64_t)) {
      if (errno != EAGAIN) {
          LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
                  mWakeEventFd, strerror(errno));
      }
  }
}

其中關(guān)鍵邏輯是對 mWakeEventFd 發(fā)起寫入操作,從而喚醒 nativePollOnce 中通過 epoll_wait 進(jìn)入休眠的線程。

結(jié)束

回答剛開始的疑問handler中的休眠/喚醒不用Java中wait和notify呢,而是調(diào)用nativePollOnce/nativeWake呢?

MessageQueue.java
//調(diào)用MessageQueue的next方法獲取消息
Message next() {
      、、、
      synchronized (this) {
          //這時候檢查隊列有沒有消息,沒有消息調(diào)用this.wait()等待  
          if (message == null) {
              this.wait();
          }
          if(有消息但消息未到期){
            this.wait(time);
          }
      }
      、、、
  }
//調(diào)用MessageQueue.enqueueMessage()添加消息
enqueueMessage(Message message) {
    、、、
    synchronized (this) {
        //消息加入隊列后會調(diào)用this.notity()喚醒next()方法
        if (message != null) {
            this.notify();
        }
    }
      、、、
  }

private native static long nativeInit(); 返回值是nativeMessage對象,阻塞時會將mPtr當(dāng)成參數(shù)nativePollOnce();

如果單純用object.wait,那對于native層的消息是處理不到的,隊列空閑時不能只判斷Java層的MessageQueue,nativePollOnce去判斷Native層,若大家都空閑,方法會阻塞到native的epoll_wait()方法中,等待喚醒。 單純用wait和notify,只能處理java層的消息,對于系統(tǒng)的消息不能處理。

以上就是Android Handler中的休眠喚醒實現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于Android Handler休眠喚醒的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • android設(shè)備間實現(xiàn)無線投屏的示例代碼

    android設(shè)備間實現(xiàn)無線投屏的示例代碼

    Android提供了MediaProjection來實現(xiàn)錄屏,通過MediaProjection可以獲取當(dāng)前屏幕的視頻流,而視頻流需要通過編解碼來壓縮進(jìn)行傳輸,通過MediaCodec可實現(xiàn)視頻的編碼和解碼,這篇文章主要介紹了android設(shè)備間實現(xiàn)無線投屏,需要的朋友可以參考下
    2022-06-06
  • Android實現(xiàn)WebView點擊攔截跳轉(zhuǎn)原生

    Android實現(xiàn)WebView點擊攔截跳轉(zhuǎn)原生

    這篇文章主要介紹了Android實現(xiàn)WebView點擊攔截跳轉(zhuǎn)原生,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-03-03
  • Flutter彈性布局Flex水平排列Row垂直排列Column使用示例

    Flutter彈性布局Flex水平排列Row垂直排列Column使用示例

    這篇文章主要為大家介紹了Flutter彈性布局Flex水平排列Row垂直排列Column使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Android中MPAndroidChart自定義繪制最高點標(biāo)識的方法

    Android中MPAndroidChart自定義繪制最高點標(biāo)識的方法

    目前在做一款軟件,要求在展示走勢圖的時候?qū)ψ罡唿c進(jìn)行自定義繪制,下面這篇文章主要給大家介紹了關(guān)于Android中MPAndroidChart自定義繪制最高點標(biāo)識的方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • android自定義View實現(xiàn)圓環(huán)顏色選擇器

    android自定義View實現(xiàn)圓環(huán)顏色選擇器

    這篇文章主要介紹了android自定義View實現(xiàn)圓環(huán)顏色選擇器,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • Android學(xué)習(xí)之AppWidget筆記分享

    Android學(xué)習(xí)之AppWidget筆記分享

    這篇文章主要為大家詳細(xì)介紹了Android學(xué)習(xí)筆記之AppWidget的相關(guān)資料,感興趣的小伙伴們可以參考一下
    2016-08-08
  • Android實現(xiàn)文本排版

    Android實現(xiàn)文本排版

    這篇文章主要介紹了Android實現(xiàn)文本排版,對多行文本進(jìn)行排版布局,每一行的內(nèi)容又分為兩部分,左邊為標(biāo)題,右邊為描述,左邊內(nèi)容長度不確定,右邊的內(nèi)容需要對齊,需要的朋友可以參考下
    2016-04-04
  • Android 自定義TextView實現(xiàn)滑動解鎖高亮文字

    Android 自定義TextView實現(xiàn)滑動解鎖高亮文字

    這篇文章主要介紹了Android 自定義TextView實現(xiàn)滑動解鎖高亮文字的相關(guān)資料,需要的朋友可以參考下
    2018-03-03
  • Flutter?文字中劃線動畫StrikeThroughTextAnimation

    Flutter?文字中劃線動畫StrikeThroughTextAnimation

    這篇文章主要為大家介紹了Flutter?文字中劃線動畫StrikeThroughTextAnimation示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • android仿知乎標(biāo)題欄隨ScrollView滾動變色

    android仿知乎標(biāo)題欄隨ScrollView滾動變色

    這篇文章主要為大家詳細(xì)介紹了android仿知乎標(biāo)題欄隨ScrollView滾動變色,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06

最新評論

九龙坡区| 永宁县| 个旧市| 天全县| 丽江市| 图木舒克市| 册亨县| 襄城县| 晋宁县| 吴川市| 巴青县| 滦南县| 和政县| 白银市| 富阳市| 汤原县| 乌拉特前旗| 读书| 阳江市| 当涂县| 舟曲县| 信宜市| 柳河县| 湘潭县| 大港区| 云梦县| 子洲县| 孙吴县| 钟祥市| 尼玛县| 治多县| 普兰县| 东乡| 太仆寺旗| 清新县| 红河县| 南投市| 新蔡县| 潮州市| 高青县| 新沂市|