Android 定時任務的8種實現(xiàn)方法
功能分析
功能描述
每隔5秒,打印一句,我愛你中國。
環(huán)境分析
我們知道,Android中分主線程(UI線程)和子線程,子線程無法操作UI的改變,我們目前不考慮UI問題,也不考慮線程通信問題,就考慮有多少方法可以實現(xiàn)上述功能。
功能分解
每需要實現(xiàn)一個功能的時候,我們在寫代碼前分析一下實現(xiàn)功能所需要的步驟,這樣有利于我們用更清晰明了的邏輯來實現(xiàn)功能。根據(jù)功能描述,要實現(xiàn)此功能,可以拆分為兩步:
- 打印 "我愛你中國" 這句話。
打印代碼不需要討論,我們就是用System.out.println("我愛你中國");這句就好。
- 每隔5秒的實現(xiàn)。
每隔5秒,需要通知打印的代碼執(zhí)行,所以我們需要實現(xiàn)一個帶有定時發(fā)送指令或者定時可以執(zhí)行邏輯的功能。
方案制定
基于上面的分析,所以我們需要在Android、java中找到可以帶有定時邏輯的相關(guān)API,從原生java SDK到Android SDK自帶API來一一列舉:
以下是Java SDK
- while循環(huán)+sleep
- 遞歸+sleep
- Timer、TimerTask
- ScheduledExecutorService(帶有定時任務的線程池)
//以下是Android SDK
- Handler循環(huán)發(fā)消息
- Handler的postDelayed方法
- BroadcastReceiver循環(huán)自發(fā)廣播
- AlarmManger+BroadcastReceiver定時發(fā)送廣播
功能實現(xiàn)
經(jīng)過上面的分析,我們已經(jīng)制定了8種實現(xiàn)方案,接下來我們依次寫出實現(xiàn)代碼并分析優(yōu)缺點。
while循環(huán)+sleep
- 代碼實現(xiàn)
while(true){
System.out.println("我愛你中國");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- 分析
優(yōu)點:代碼簡單,邏輯清晰
缺點:1、sleep會阻塞線程,也有被打斷的風險
? 2、while(true)下面的代碼無法執(zhí)行,野路子
適用場景:間隔時間較短(秒級的),循環(huán)執(zhí)行次數(shù)較少(有退出機制或者用for循環(huán)),執(zhí)行邏輯簡單
遞歸+sleep
- 代碼實現(xiàn)
public void print() {
System.out.println("我愛你中國" );
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
print();//遞歸調(diào)用自身
}
- 分析
優(yōu)點:代碼簡單,邏輯清晰
缺點:1、sleep會阻塞線程,也有被打斷的風險
? 2、遞歸次數(shù)過多會出現(xiàn)java.lang.StackOverflowError堆棧溢出的異常,所以嚴格來說不算定時任務
適用場景:間隔時間較短(秒級的),循環(huán)執(zhí)行次數(shù)較少(有退出機制或者用for循環(huán))
Timer、TimerTask
- 代碼實現(xiàn)
public class TimerTest {
static class MyTimerTask extends TimerTask {
public void run() {
System.out.println("我愛你中國");
}
}
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new MyTimerTask(), 0, 5000);
}
}
- 分析
優(yōu)點:代碼正宗,沒有野路子,純正的定時任務,純java SDK,單獨線程執(zhí)行,時間周期可以長些
缺點:1、Timer還有一個傳入Date的方法,此方法是基于絕對時間,系統(tǒng)時間改變對應的時間也會改變
? 2、Timer線程不捕獲異常,執(zhí)行過程中出現(xiàn)異常就會終止Timer任務
? 3、手機關(guān)屏之后,CPU進入休眠,Timer無法喚醒CPU(據(jù)說)
適用場景:簡單的定時任務,對于時間要求不是很精確,執(zhí)行過程中處理好異常的捕獲??捎胻imer.cancel()取消
ScheduledExecutorService
- 代碼實現(xiàn)
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("我愛你中國");
}
},0,5,TimeUnit.SECONDS);
- 分析
優(yōu)點:ScheduledExecutorService是一個線程池,多任務處理時效率高,基于相對時間,看起來高大上。
缺點:1、取消時需要打斷線程池的運行
? 2、和外界的通信不太好處理
? 3、Android中適用度不高(android中會用AlarmManger+Service+BarocastReceiver替代)
適用場景:適合那種長期、定期執(zhí)行的任務
Handler循環(huán)發(fā)消息
- 代碼實現(xiàn)
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("我愛你中國");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0);
}
};
//調(diào)用
handler.sendEmptyMessage(0);
- 分析
優(yōu)點:純Android SDK,邏輯清晰
缺點:這種循環(huán)執(zhí)行不適用于復雜任務,不適用于多次循環(huán)的,偽定時,sleep缺點上面說過了,Handler依賴其所在線程
適用場景:單純這么寫是沒有適用場景,Handler就老老實實的實現(xiàn)消息分發(fā)吧
Handler的postDelayed方法
- 代碼實現(xiàn)
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("我愛你中國");
handler.sendEmptyMessageDelayed(0,5000);
}
};
//調(diào)用
handler.sendEmptyMessageDelayed(0,5000);
- 分析
上面以上,沒啥分析的
BroadcastReceiver循環(huán)自發(fā)廣播
- 代碼實現(xiàn)
public static final String TEST_ACTION = "XXX.XXX.XXX" + "_TEST_ACTION";
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TEST_ACTION.equals(action)) {
System.out.println("我愛你中國");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
context.sendBroadcast(intent);
}
}
};
//調(diào)用
IntentFilter filter = new IntentFilter();
filter.addAction(TEST_ACTION);
registerReceiver(receiver, filter);
//取消
unregisterReceiver(receiver);
- 分析
優(yōu)點:Android SDK的代碼,廣播監(jiān)聽機制
缺點:單純這么寫就是缺點
適用場景:BroadcastReceiver適用于接受廣播,執(zhí)行一定的邏輯,可以根據(jù)注冊方式不同適用不同的場景。
AlarmManger+BroadcastReceiver
- 代碼實現(xiàn)
public class MyService extends Service {
int TIME_INTERVAL = 5000; // 這是5s
PendingIntent pendingIntent;
AlarmManager alarmManager;
@Override
public void onCreate() {
super.onCreate();
IntentFilter intentFilter = new IntentFilter(TEST_ACTION);
registerReceiver(receiver, intentFilter);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent();
intent.setAction(TEST_ACTION);
pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//6.0低電量模式需要使用該方法觸發(fā)定時任務
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//4.4以上 需要使用該方法精確執(zhí)行時間
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);
} else {//4。4一下 使用老方法
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), TIME_INTERVAL, pendingIntent);
}
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static final String TEST_ACTION = "XXX.XXX.XXX" + "_TEST_ACTION";
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TEST_ACTION.equals(action)) {
System.out.println("我愛你中國");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TIME_INTERVAL, pendingIntent);
}
}
}
};
}
- 分析
優(yōu)點:這是標準的Android觸發(fā)定時任務的方式,依賴的是Android系統(tǒng)服務,有效喚醒
缺點:1、都說標準了,哪還有啥缺點(AlarmManager喚醒type字段可以區(qū)分是按照手機開機時間還是系統(tǒng)時間,系統(tǒng)時間受系統(tǒng)時間修改的影響,同時也有不喚醒CPU的type)
? 2、4.4以下,4.4到6.0,6.0以上這個類變動的較多,需要區(qū)分版本使用。
適用場景:Android應用中常見的定時任務,鬧鐘等等場景。
以上!