android定時任務的
⑴ android怎麼設置一個定時的任務
我知道的有三種方法;
方法一:可以通過android自帶的Handler機制,裡面有new Handler().postDelayed(new Runnable(){
@Override
public void run() {
//延遲的時間到了執行
}
},延遲的時間);
方法二:可以通過java裡面的線程來寫,new Thread().sleep(延遲時間);時間到了會繼續執行;
方法三:java定時器(有很多方法)例如:
Timer timer = new Timer();
timer.schele(new TimerTask() {
public void run() {
System.out.println("-------設定要指定任務--------");
}
}, long delay,long period);
delay延遲的時間,period為多久執行一次;
個人推薦用方法一,畢竟安卓系統自帶的,並且安卓不適合加定時器,如果處理不當一直執行的話,要麼卡,要麼程序崩潰。還有發布版本可能審核不通過。
⑵ android定時器的使用
目前在項目中使用過的定時器主要是兩種方式:1.handler的延時任務 2.timer的定時器
下面主要來介紹這兩種方式 定時器的用法,和遇到的坑 :
1.handler的延時任務
寫了一個demo,是關於hanlder發送延時消息和 取消延時消息的
但是在實際運用的過程中,常常會出現無法移除延時消息的問題,這個問題主要是因為兩個runnable的對象不是同一個,
當Activity進入後台運行後再轉入前台運行,removeCallbacks無法將updateThread從message queue中移除。這是為什麼呢?
在Activity由前台轉後台過程中,線程是一直在運行的,但是當Activity轉入前台時會重新定義Runnable runnable;也就是說此時從message queue移除的runnable與原先加入message queue中的runnable並非是同一個對象。 解決方式1:如果把runnable定義為靜態的則removeCallbacks不會失效,對於靜態變數在內存中只有一個拷貝(節省內存),JVM只為靜態分配一次內存,在載入類的過程中完成靜態變數的內存分配,我們做如下修改就能解決上面的這個問題.
解決方式2:將所有的runnable任務放到一個集合中,根據runnable的id移除相應的任務,即可以解決這個問題
2.timer的延時任務
同樣用上面的demo來說明使用方法:
要注意的是:
TimerTask運行在一個單獨的線程里,而不是UI線程。所以使用 Android timer時,注意android的單線程原則,確保線程安全。不要在TimerTask的run方法中做UI相關的操作,如:TextView.setText()等,這樣可能會導致UI線程阻塞。如果需要可以使用handler向UI線程發消息,具體處理由UI線程自己完成。在使用完Timer之後,要使用Timer的cancel方法取消Timer,否則Timer一直在運行。
這里需要注意的是,timer的cancel方法只能執行一次,不能在調用了cancel之後再進行timer的任務執行。
參考文章:
Android 定時器實現的幾種方式和removeCallbacks失效問題詳解
Android 中Timer和TimeTask完成定時任務
⑶ Android 實現定時任務的五種方式
1、普通線程sleep的方式,可用於一般的輪詢Polling
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
//todo
try {
Thread.sleep(iDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
優點:非常簡單的實現,邏輯清晰明了,也是最常見的寫法
缺點:在sleep結束後,並不能保證競爭到cpu資源,這也就導致了下次執行時間必定>=iDelay,存在時間精度問題
2、Timer定時器
//Timer + TimerTask結合的方法
private final Timer timer = new Timer();
private TimerTask timerTask = new TimerTask() {
@Override
public void run() {
//todo
}
};
啟動定時器方法:
timer.schele(TimerTask task, long delay, long period)
立即執行
timer.schele(timerTask, 0, 1000); //立刻執行,間隔1秒循環執行
延時執行
timer.schele(timerTask, 2000, 1000); //等待2秒後再執行,間隔1秒循環執行
關閉定時器方法:timer.cancel();
優點:純正的定時任務,純java SDK,單獨線程執行,比較安全,而且還可以在運行過程中取消執行
缺點:基於單線程執行,多個任務之間會相互影響,多個任務的執行是串列的,性能較低,而且timer也無法保證時間精確度,是因為手機休眠的時候,無法喚醒cpu,不適合後台任務的定時
3、ScheledExecutorService
private Runnable runnable2 = new Runnable() {
@Override
public void run() {
//todo
}
};
ScheledExecutorService executor = Executors.newScheledThreadPool(1);
executor.scheleAtFixedRate(runnable2, 0, 1, TimeUnit.SECONDS);
關於scheleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 方法說明:
command:需要執行的線程
initialDelay:第一次執行需要延時的時間,如若立即執行,則initialDelay = 0
period:固定頻率,周期性執行的時間
unit:時間單位,常用的有MILLISECONDS、SECONDS和MINUTES等,需要注意的是,這個單位會影響initialDelay和period,如果unit = MILLISECONDS,則initialDelay和period傳入的是毫秒,如果unit = SECONDS,則initialDelay和period傳入的是秒
補充一下: 還有一個方法跟上面的很相似:scheleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit),這個也是帶延遲時間的調度,並且也是循環執行,唯一的不同就是固定延遲時間循環執行,上面的是固定頻率的循環執行。那這兩者的區別?
舉例子:
使用scheleAtFixedRate,任務初始延遲3秒,任務執行3秒,任務執行間隔為5秒:
ScheledExecutorService executor = Executors.newScheledThreadPool(1);
Log.e(TAG, "schele just start! time =" + simpleDateFormat.format(System.currentTimeMillis()));
executor.scheleAtFixedRate(new Runnable() {
@Override
public void run() {
SystemClock.sleep(3000L);
Log.e(TAG, "runnable just do it! time =" + simpleDateFormat.format(System.currentTimeMillis()));
}
}, 3, 5, TimeUnit.SECONDS);
執行結果截圖:
使用scheleWithFixedDelay,任務初始延遲3秒,任務執行3秒,任務執行延遲為5秒:
ScheledExecutorService executor = Executors.newScheledThreadPool(1);
Log.e(TAG, "schele just start! time =" + simpleDateFormat.format(System.currentTimeMillis()));
executor.scheleWithFixedDelay(new Runnable() {
@Override
public void run() {
SystemClock.sleep(3000L);
Log.e(TAG, "runnable just do it! time =" + simpleDateFormat.format(System.currentTimeMillis()));
}
}, 3, 5, TimeUnit.SECONDS);
執行結果截圖:
從這兩者的運行結果就可以看到區別了:scheleAtFixedRate是相對於任務執行的開始時間,而scheleWithFixedDelay是相對於任務執行的結束時間。
優點:ScheledExecutorService是一個線程池,其內部使用的延遲隊列,本身就是基於等待/喚醒機制實現的,所以CPU並不會一直繁忙。解決了Timer&TimerTask存在的問題,多任務處理時效率高
缺點:取消時需要打斷線程池的運行,而且和外界的通信不太好處理
4、使用Handler中的postDelayed方法
private Handler mHandler = new Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
//todo
mHandler.postDelayed(this, iDelay);
}
};
mHandler.post(runnable); //立即執行
mHandler.postDelayed(runnable, iDelay); //延時執行
mHandler.removeCallbacks(runnable); //取消執行
優點:比較簡單的android實現,適用UI線程
缺點:沒想到,手動捂臉。。。。我估計是使用不當會造成內存泄露吧
5、Service + AlarmManger + BroadcastReceiver
⑷ android 後台運行 並定時觸發任務
Android中的定時任務一般有兩種實現方式,一種是使用Java
API里的Timer類,另一種是使用Android的Alarm機制。
這兩種方式在多數情況下都能實現類似的效果,但Timer有一個明顯的短板,它並不太適用與那些需要長期在後台運行的定時任務。As we
know,為了能讓電池更加耐用,每種手機都會有自己的休眠策略:比如手機不用的時候智能的斷開wifi連接,根據光線強弱自動調節屏幕亮度,根據手機長時間無操作時自動的讓CPU進入到休眠狀態等,當進入休眠狀態時,這就有可能導致Timer中的定時任務無法正常運行。而Alarn機制則不存在這種情況,它具有喚醒CPU的功能,即可以保證每次需要執行定時任務的時候CPU都能正常工作。需要注意的是,這里的喚醒CPU和喚醒屏幕不是同一個概念,不能混淆。
這里我們只說Alarm機制的方式,代碼如下:
public class AutoUpdateService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
// 每次服務啟動的時候調用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
doSomething();//這是定時所執行的任務
}
}).start();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int anHour =8 * 60 * 60 * 1000;// 這是8小時的毫秒數 為了少消耗流量和電量,8小時自動更新一次
long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
Intent intent2 = new Intent(this, AutoUpdateReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, intent2, 0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);</span>
return super.onStartCommand(intent, flags, startId);
}
注意這里的AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);此處實現了定時任務。
首先我們通過調用Context的getSystemService()方法來獲取AlarmManager的實例,這里需要傳入的參數是ALARM_SERVICE.
接下來調用AlarmManager的set()方法就可以設置一個定時任務了,比如設定一個任務在5秒鍾後執行,就可以寫成 long
triggerAtTime = SystemClock.elapsedRealtime() + 5*1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime,
pi);
其中第一個參數是一個整形參數,用於指定AlarmManager的工作類型,有四種值可以選,分別是
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()方法可以獲取到系統開機至今所經歷時間的毫秒數,使用System.currentTimeMillis()方法可以獲取到1970年1月1日0點至今所經歷時間的毫秒數。
然後看一下第二個參數,這個參數就好理解多了,就是定時任務觸發的時間,以毫秒為單位。如果第一個參數使用的是ELAPSED_REALTIME或ELAPSED_REALTIME_WAKEUP,則這里傳入開機至今的時間再加上延遲執行的時間。如果第一個參數使用的是RTC或RTC_WAKEUP,則這里傳入1970年1月1日0點至今的時間再加上延遲執行的時間。
第三個參數是一個PendingIntent,這里我們一般會調用getBroadcast()方法來獲取一個能夠執行廣播的PendingIntent。這樣當定時任務被觸發的時候,廣播接收器的onReceive()方法就可以得到執行。
當然設定一個任務在10秒鍾後執行還可以寫成:
longtriggerAtTime =
System.currentTimeMillis() + 10 * 1000;
manager.set(AlarmManager.RTC_WAKEUP,triggerAtTime,
pendingIntent);
然後創建PendingIntent指定處理定時任務的廣播接收器AutoUpdateReceiver。
import service.AutoUpdateService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class AutoUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, AutoUpdateService.class);
context.startService(i);
}
}
當啟動AutoUpdateService後,就會在onStartCommand()方法里設定一個定時任務,這樣每8個小時AutoUpdateReceiver的onReceive()方法就會得到執行,這樣就又會啟動AutoUpdateService服務,形成了永久的循環,保證服務每隔一段時間就會啟動一次,這樣就完成了一個長期在後台運行的服務。
我們在哪裡啟動服務呢,這要看具體的情況了,一般的話,當我們打開程序的時候啟動一次就好了
比如寫在Activity的onCrete()方法里
Intent
intent =new Intent(this,AutoUpdateService.class);
startService(intent);
最後,既然我們用到了服務和廣播接收器,那麼就得在AndroidManifest.xml中注冊才行。
<service android:name="service.AutoUpdateService" ></service>
<receiver android:name="receiver.AutoUpdateReceiver"
></receiver>
本文參考資料:《第一行代碼》