乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      (原創(chuàng))android Framework中AlarmManagerSerevice分析理解

       老匹夫 2016-02-18
       1.簡介

      Android系統(tǒng)中,鬧鐘和定時喚醒功能都是由AlarmManagerService控制并管理的。我們所熟悉的RTC鬧鐘都和它有很大的關(guān)系。為了便于稱呼,把這個service稱為ALMS(為了和 ActivityManagerService區(qū)別)。

       

      Alarm與系統(tǒng)中其他服務(wù)一樣,都是客戶端/服務(wù)端模型,AlarmManager向上層提供調(diào)用的接口,具體功能實現(xiàn)在AlarmManagerService中,Alarm實現(xiàn)代碼主要是在管理邏輯鬧鐘上,它把鬧鐘分為幾大類分別記錄再不同的列表中,然后在ALMS中開啟一個專門的線程AlarmThread來循環(huán)的等待鬧鐘的出發(fā),一旦時間到了,就會回調(diào)邏輯鬧鐘的動作,并發(fā)送給應(yīng)用處理。

       

      2.AlarmManager

      AlarmManagerService是服務(wù)端的代碼,向外提供具體接口代碼,是AlarmManager,通過aidl機制,IAlarmManager來調(diào)用到AlarmManagerService,IAlarmManager。其定義位于frameworks\base\core\java\android\app\IAlarmManager.aidl腳本中,定義截選如下:

      interface IAlarmManager {

          void set(int type, long triggerAtTime, in PendingIntent operation);//在規(guī)定的時間精確的執(zhí)行鬧鐘,這個函數(shù)應(yīng)該是鬧鐘執(zhí)行精度比較高

       

          void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);//該方法用于設(shè)置重復鬧鐘,第一個參數(shù)表示鬧鐘類型,第二個參數(shù)表示觸發(fā)這個鬧鐘要等待的時間,第三個參數(shù)表示鬧鐘兩次執(zhí)行的間隔時間,第三個參數(shù)表示鬧鐘響應(yīng)動作。

       

          void setInexactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);//設(shè)置一個重復鬧鐘的不精確版本,它相對而言更節(jié)能一些,因為系統(tǒng)可能會將幾個差不多的鬧鐘合并為一個來執(zhí)行,減少設(shè)備的喚醒次數(shù)。由于不是精確版,所以這里的intervaMills

      會略有不同

      參數(shù):intervalMillis: NTERVAL_FIFTEEN_MINUTES 

      INTERVAL_HALF_HOUR

      INTERVAL_HOUR

      INTERVAL_HALF_DAY

      INTERVAL_DAY

       

          void setTime(long millis);//設(shè)置系統(tǒng)時間

          void setTimeZone(String zone);//設(shè)置系統(tǒng)的默認時區(qū)。需要android. Permission. SET_TIME_ZONE權(quán)限。

       

          void remove(in PendingIntent operation);//刪除一個Alarm

      }

       

      在應(yīng)用需要使用Alarm服務(wù)時,會通過ServiceManager去獲取Alarm服務(wù),在一般情況下,service的使用者會通過Service Manager Service接口,先拿到它感興趣的service對應(yīng)的代理I接口,然后再調(diào)用I接口的成員函數(shù)向service發(fā)出請求。所以按理說,我們也應(yīng)該先 拿到一個IAlarmManager接口,然后再使用它??墒?,對Alarm Manager Service來說,情況略有不同,其最常見的調(diào)用方式如下:

      manager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

       

      其中,getSystemService()返回的不再是IAlarmManager接口,而是AlarmManager對象。我們參考AlarmManager.java文件,可以看到AlarmManager類中聚合了一個IAlarmManager接口:

      private final IAlarmManager mService;

       

      也就是說在執(zhí)行實際動作時,AlarmManager只不過是把外界的請求轉(zhuǎn)發(fā)給內(nèi)部聚合的IAlarmManager接口而已。

        另外,AlarmManager類中會以不同的公共常量來表示多種不同的邏輯鬧鐘,在Android 4.0的原生代碼中有4種邏輯鬧鐘:

      1) RTC_WAKEUP

      2) RTC

      3) ELAPSED_REALTIME_WAKEUP

      4) ELAPSED_REALTIME

      應(yīng)用通過調(diào)用AlarmManager對象的成員函數(shù),可以傳遞到AlarmManagerService,并由它進行實際的處理。

       

      3.AlarmManagerService

      ALMS主要工作是在AlarmManagerService中,這個類繼承于IAlarmManager.Stub,所以它是一個Binder實體,,所以主要代碼都是在AlarmManagerService重視先的,當Alarm被設(shè)置之后,會被存入mPendingNonWakeupAlarms的一個Alarm的列表中,

      3.1.AlarmManagerService的啟動

      1.ALMS的是一個系統(tǒng)級服務(wù),啟動過程是在SystemServer.java中的StartOtherService中調(diào)用到SystemManagerService的startService方法啟動服務(wù)。

      2.startService方法中,先調(diào)用到服務(wù)類的構(gòu)造函數(shù)完成初始化,然后調(diào)用到服務(wù)類的onstart方法啟動。

      3.onStart方法中申請PowerManager的PARTIAL_WAKE_LOCK鎖,初始化mTimeTickSender,(一個PendingIntent,發(fā)送時間改變的廣播,每到整分鐘發(fā)送一次),初始化的mDateChangeSender(一個PendingIntent,發(fā)送日期改變的廣播,每天發(fā)送一次),創(chuàng)建ClockReceiver對象,來處理時間和日期改變的廣播事件。

      4.最后在onStart中將ALMS注冊到Binder服務(wù)端。

       

      3.2邏輯鬧鐘

      Alarm是AlarmManagerService的一個內(nèi)部類Alarm,所有應(yīng)用在設(shè)置Alarm的時候,都會在setImplLocked函數(shù)中將Alarm格式化為內(nèi)部類Alarm的格式,定義截選如下:

          private static class Alarm {

          public int type;

          public int count;

          public long when;

          public long repeatInterval;

          public PendingIntent operation;

          public int uid;

          public int pid;

      其中記錄了邏輯鬧鐘的一些關(guān)鍵信息。 

      type域:記錄著邏輯鬧鐘的鬧鐘類型,比如RTC_WAKEUP、ELAPSED_REALTIME_WAKEUP等;

      count域:是個輔助域,它和repeatInterval域一起工作。當repeatInterval大于0時,這個域可被用于計算下一次重復激發(fā)alarm的時間。

      when域:記錄鬧鐘的激發(fā)時間。這個域和type域相關(guān),詳細情況見后文;

      repeatInterval域:表示重復激發(fā)鬧鐘的時間間隔,如果鬧鐘只需激發(fā)一次,則此域為0,如果鬧鐘需要重復激發(fā),此域為以毫秒為單位的時間間隔;

      operation域:記錄鬧鐘激發(fā)時應(yīng)該執(zhí)行的動作,詳細情況見后文;

      uid域:記錄設(shè)置鬧鐘的進程的uid;

      pid域:記錄設(shè)置鬧鐘的進程的pid。

       

       

      3.2設(shè)置Alarm

      外界應(yīng)用設(shè)置Alarm的函數(shù)是set():

        public void set(int type, long triggerAtTime, PendingIntent operation)

      type:表示要設(shè)置的alarm類型。如前文所述,有4個alarm類型。 

      triggerAtTime:表示alarm“理應(yīng)激發(fā)”的時間。 

      operation:指明了alarm鬧鈴激發(fā)時需要執(zhí)行的動作,比如執(zhí)行某種廣播通告。

       

      一般是應(yīng)用調(diào)用AlarmManager的set方法,然后通過AlarmManagerService中去設(shè)置一個Alarm請求,并且在請求中注明了到達的時間以及要執(zhí)行的動作,由于等待執(zhí)行的動作一般不會馬上執(zhí)行,所以用PendingIntent形式,去發(fā)送一個廣播或者Activity,當?shù)竭_時間時間之后,應(yīng)用受到來自Alarm的PendingIntent的廣播,會執(zhí)行PendingIntent中包含的動作。

       

      3.3重復性的Alarm

      另一個設(shè)置alarm的函數(shù)是setRepeating():

          public 

          void setRepeating(int type, 

          long triggerAtTime, long interval,PendingIntent operation)

      其參數(shù)基本上和set()函數(shù)差不多,只是多了一個“時間間隔”參數(shù)。事實上,在Alarm Manager Service一側(cè),set()函數(shù)內(nèi)部也是在調(diào)用setRepeating()的,只不過會把interval設(shè)成了0。同樣調(diào)到setImpl中。在rescheduleKernelAlarmsLocked();會調(diào)用setLocked() ,setLocked會調(diào)用到native中的set方法,

      重新設(shè)置“實體鬧鐘”的激發(fā)時間。這個函數(shù)內(nèi)部會調(diào)用ioctl()和底層打交道。具體代碼可參考:

      frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp文件:

        static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jint fd, 

       

      jint type, jlong seconds, jlong nanoseconds)

       

      {

       

          struct timespec ts;

       

          ts.tv_sec = seconds;

       

          ts.tv_nsec = nanoseconds;

       

          int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts);

       

          if (result < 0)

       

          {

       

              ALOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno));

       

          }

       

      }

       我們知道,PendingIntent只是frameworks一層的概念,和底層驅(qū)動是沒有關(guān)系的。所以向底層設(shè)置alarm時只需要type信息以及激發(fā)時間信息就可以了。

       

      3.4 AlarmBatches分析

      Alarm默認為非精準模式,除非顯示指定采用精準模式。在非精準模式下,Alarm是批量提醒的,每個alarm根據(jù)其觸發(fā)時間和最大觸發(fā)時間的不同會被加入到不同的batch中,每個Batch都是一個開始時間和結(jié)束時間,在開始和結(jié)束之間的Alarm都會被存在該batch中,所以同一個batch中有不同alarm,但是同一個batch中的Alarm是同時發(fā)生的,這樣雖然無法實現(xiàn)精準鬧鐘,但是官方的解釋是批量處理可以減少設(shè)備被喚醒次數(shù)以及,降低功耗;其關(guān)系示意圖如下:

       

       

      不過針對精準鬧鐘,官方預留的方法是setExact和setWindow,二者都是通過將時間窗口定義為0來實現(xiàn)精準鬧鐘的,因為時間窗口為0,意味著觸發(fā)時間和最大觸發(fā)時間是一樣的,因為典型的情況下:最大觸發(fā)時間= 觸發(fā)時間 + 窗口時間。同時所有的batch是按開始時間升序排列的,在一個batch內(nèi)部,不同的鬧鐘也是按觸發(fā)時間升序排列的,所以鬧鐘的喚醒順序是按照 batch的排序依次觸發(fā)的,而同一個batch中的alarm是同時觸發(fā)的。

       

      在第三方應(yīng)用通過setImpl來設(shè)置Alarm時候,僅僅是將這個alarm加入到某個batch中,

      在AlarmManagerService中創(chuàng)建了一個Batch列表,專門存儲batch,batch中再存儲Alarm,系統(tǒng)中新創(chuàng)建一個線程AlarmThread去遍歷該列表,如果發(fā)現(xiàn)出發(fā)時間到了,就將其取出來,執(zhí)行該Alarm,此流程在后文詳細分析

       

       

      4.AlarmThread和Alarm的激發(fā)

      AlarmManagerService在啟動的時候就創(chuàng)建了一個線程waitThread并直接啟動它,在onStart()函數(shù)中:

           mNativeData = init();

      …...

                  AlarmThread waitThread = new AlarmThread();

                  waitThread.start();

      在onStart函數(shù)之初就回調(diào)用一個init()函數(shù),這個函數(shù)是一個native函數(shù),內(nèi)部會打開Alarm驅(qū)動,并返回驅(qū)動文件句柄,只要能夠順利打開Alarm驅(qū)動,ALMS就可以啟動waitThread線程,

      AlarmThread本身是AlarmManagerService中繼承Thread的內(nèi)部類:

      其AlarmThread線程run方法中是一個while(true)死循環(huán)

      其流程如下:

       

       

      4.1 waitforAlarm()

      waitForAlarm()函數(shù)是一個native層的函數(shù),它使用來等待底層alarm激發(fā)動作的。觸發(fā)Alarm的重要值存在result中傳給上層, 上層利用這個result來遍歷Alarm列表。

       

      4.2 triggerAlarmsLocked()

      一旦等到底層驅(qū)動的激發(fā)動作,AlarmThread會開始遍歷相應(yīng)的邏輯鬧鐘列表,AlarmThread先創(chuàng)建一個臨時的數(shù)組列表triggerList,然后根據(jù)result的值對相應(yīng)的alarm數(shù)組列表調(diào)triggerAlarmsLocked(),一旦發(fā)現(xiàn)alarm數(shù)組列表中有某個alarm符合激發(fā)條件,就把它移到triggerList中。這 樣,4條alarm數(shù)組列表中需要激發(fā)的alarm就匯總到triggerList數(shù)組列表中了。

      在deliverAlarmsLocked函數(shù)中遍歷到Alarm的triggerList,每遍歷到一個Alarm,就執(zhí)行它的alarm.operation.send()函數(shù)。我們知道,alarm中記錄的operation就是當初設(shè)置它時傳來的那個PendingIntent對象,現(xiàn)在開始執(zhí)行PendingIntent的send()操作。

       

      在deliverAlarmsLocked中最后會判斷Alarm是否是ELAPSED_REALTIME_WAKEUP或者是RTC_WAKEUP,就屬于喚醒鬧鐘,就會調(diào)用到ActivityManagerNative.noteWakeupAlarm,去通過BatteryStatsImpl來統(tǒng)計喚醒的Alarm,放入一個mWakeupAlarm的list中。

       

      5.總結(jié)

      AlarmManager字面意思是鬧鐘管理,一些與時間相關(guān)的應(yīng)用,如日歷,鬧鐘等需要使用Alarm Manager的服務(wù),但是在整個Android中,周期性的喚醒,以及定時喚醒不僅僅只是鬧鐘會用到,第三方應(yīng)用的推送服務(wù)以及和定時和服務(wù)器交互的心跳數(shù)據(jù)包,也會用到。但是流程都是在設(shè)置定時執(zhí)行的任務(wù)時,將Intent封裝在PendingIntent中,觸發(fā)時間到了之后,從底層接受到觸發(fā)動作之后,通過獲取PendingIntent,來執(zhí)行定時任務(wù)。

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多