標籤:ice flags ide 類型 ems 完成 display 停止 決策
Android中的定時任務一般有兩種實現方式,一種是使用Java API裡的Timer類,另一種是使用android的Alarm機制。
這兩種方式在多數情況下都能實作類別似的效果,但Timer有一個明顯的短板,它並不太適用與那些需要長期在後台啟動並執行定時任務。As we know,為了能讓電池更加耐用,每種手機都會有自己的休眠策略:比如手機不用的時候智能的斷開wifi串連,根據光纖強弱自動調節螢幕亮度,根據手機長時間無操作時自動的讓CPU進入到休眠狀態等,當進入休眠狀態時,這就有可能導致Timer中的定時任務無法正常運行。而Alarn機制則不存在這種情況,它具有喚醒CPU的功能,即可以保證每次需要執行定時任務的時候CPU都能正常工作。需要注意的是,這裡的喚醒CPU和喚醒螢幕不是同一個概念,不能混淆。
Timer類
使用Timer及TimerTask可以在某個時間執行某一個任務。 Timer是一個普通的類,有幾個重要的方法。而TimerTask是一個抽象類別,要使用的時候必須實現它的Run方法。TimerTask的run方法類似於線程的run方法。 我們使用Timer建立一個他的對象,然後使用這對象的schedule方法來完成這種間隔的操作。 schedule方法有三個參數
第一個參數就是TimerTask類型的對象,我們實現TimerTask的run()方法就是要周期執行的一個任務;
第二個參數有兩種類型,第一種是long類型,表示多長時間後開始執行,另一種是Date類型,表示從那個時間後開始執行;
第三個參數就是執行的周期,為long類型。
schedule方法還有一種兩個參數的執行重載,第一個參數仍然是TimerTask,第二個表示為long的形式表示多長時間後執行一次,為Date就表示某個時間後執行一次。 Timer就是一個線程,使用schedule方法完成對TimerTask的調度,多個TimerTask可以共用一個Timer,也就是說Timer對象調用一次schedule方法就是建立了一個線程,並且調用一次schedule後TimerTask是無限制的迴圈下去的,使用Timer的cancel()停止操作。當然同一個Timer執行一次cancel()方法後,所有Timer線程都被終止。
1 //true 說明這個timer以daemon方式運行(優先順序低,程式結束timer也自動結束) 2 java.util.Timer timer = new java.util.Timer(true); 3 TimerTask task = new TimerTask() { 4 public void run() { 5 //每次需要執行的代碼放到這裡面。 6 } 7 }; 8 9 10 //以下是幾種調度task的方法: 11 12 //time為Date類型:在指定時間執行一次。 13 timer.schedule(task, time); 14 15 //firstTime為Date類型,period為long,表示從firstTime時刻開始,每隔period毫秒執行一次。 16 timer.schedule(task, firstTime, period); 17 18 //delay 為long類型:從現在起過delay毫秒執行一次。 19 timer.schedule(task, delay); 20 21 //delay為long,period為long:從現在起過delay毫秒以後,每隔period毫秒執行一次。 22 timer.schedule(task, delay, period);
Alarm機制
1.首先擷取到AlarmManager的執行個體。
AlarmManager manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE)
然後調用AlarmManager的set()方法就可以設定一個定時任務了。long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 100;manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtime,pendingIntent);
set()方法中第一個參數是一個整型參數,用於指定AlarmManager的工作類型,有4種值可選,分別是ELAPSED_REALTIME、ELAPSED_REALTIME_WAKEUP、RTC和RTC_WAKEUP。
ELAPSED_REALTIME:表示讓定時任務的觸發時間從系統開機開始算起,但不會喚醒CPU。
ELAPSED_REALTIME_WAKEUP:同樣表示讓定時任務的觸發時間從系統開機時間開始算起,但會喚醒CPU。
RTC:表示讓定時任務的觸發時間從1970年1月1日0點開始算起,但不會喚醒CPU。
RTC_WAKEUP:表示讓定時任務的觸發時間從1970年1月1日0點開始算起,但會喚醒CPU。
使用SystemClock.elapsedRealtime()方法可以擷取到系統開機至今所經曆時間的毫秒數。使用SystemClock.currentTimeMills()方法可以擷取到1970年1月1日0點至今所經曆時間的毫秒數。 第二個參數是定時任務的觸發時間,以毫秒為單位。若第一個參數使用的是ELAPSED_REALTIME或是ELAPSED_REALTIME_WAKEUP那麼這裡傳入開機至今的時間再加上順延強制的時間。若第一個參數使用的是RTC或是RTC_WAKEUP,那麼這裡傳入1970年1月1日0點至今的時間再加上順延強制的時間。 第三個參數是PendingIntent。
pendingIntent是一種特殊的Intent。主要的區別在於Intent的執行立刻的,而pendingIntent的執行不是立刻的。pendingIntent執行的操作實質上是參數傳進來的Intent的操作,但是使用pendingIntent的目的在於它所包含的Intent的操作的執行是需要滿足某些條件的。主要的使用的地方和例子:通知Notificatio的發送,短訊息SmsManager的發送和警報器AlarmManager的執行等等。PendingIntent中getActivity(Context, int, Intent, int) 跳轉到一個activity組件getBroadcast(Context, int, Intent, int) 發送一個廣播組件getService(Context, int, Intent, int) 啟動一個服務元件
View Code
這裡我們一般會調用getService()方法或者getBroadcast()方法來擷取一個能夠執行服務或者廣播的PendingIntent。這樣當定時任務被觸發的時候。服務的onStartCommand()方法或者廣播的onReceive()方法就可以得到執行。
如果想要實現一個長時間在後台定時啟動並執行服務該怎麼做呢?
只需要定義一個服務,並將觸發定時任務的代碼寫到onStartCommand()方法中。
1 @Override 2 public int onStartCommand(Intent intent,int flags, int startId) { 3 new Thread(new Runnable() { 4 @Override 5 public void run() { 6 //在這裡執行具體邏輯 7 } 8 }).start(); 9 AlarmManager manager = (AlarmManager)getSystemService(ALARM_SERVICE);10 int anHour = 60 * 60 * 1000; //這是一小時的毫秒數。11 long triggerAtTimer = SystemClock.elapsedRealtime() + anHour;12 Intent i = new Intent(this,LongRunningService.class);13 PendingIntent pi = PendingIntent.getService(this,0,i,0);14 manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTimer,pi);15 return super.onStartCommand(intent,flags,startId);16 }
我們首先是在onStartCommand()方法中開啟了一個子線程,這樣就可以在這裡執行具體的邏輯操作了,之所以要在子線程裡執行邏輯操作,是因為邏輯操作也是需要耗時的,如果放在主線程裡執行可能會對
定時任務的準確性造成輕微的影響。
建立線程代碼之後,先是擷取到了AlarmManager的執行個體, 然後定義任務的觸發時間為一個小時後,再使用PendingIntent指定處理定時任務的服務為LongRunningService,最後調用set()方法完成設定。
一旦啟動LongRunningService,就會在onStartCommand()方法裡設定了一個定時任務,這樣一個小時後,將會再次啟動LongRunningService。從而形成一個永久的迴圈,保證LongRunningService的onStartCommand()方法可以每隔一個小時就執行一次。
如果想要啟動定時服務的時候調用以下代碼即可。
Intent intent = new Intent(context,LongRunningService.class); context.startService(intent);
需要注意的是,從Android4.4開始,Alarm任務的觸發時間將會變得不準確,有可能會延遲一段時間後任務才能執行,這是系統在耗電性方面的最佳化,系統會自動檢測目前有多少Alarm任務存在,然後將觸發時間相近的幾個任務放在一起執行,這樣就可以大幅度減少CPU被喚醒的次數,從而減少耗電量。
如果要求Alarm任務的執行時間必須準確無誤,只需要使用AlarmManager的setExact()方法來替代set()方法。就基本上可以保證任務能夠準時執行了。
Android下的定時任務