Android 定時任務的8種實現(xiàn)方法

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應用中常見的定時任務,鬧鐘等等場景。

以上!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、簡歷準備 1、個人技能 (1)自定義控件、UI設計、常用動畫特效 自定義控件 ①為什么要自定義控件? Andr...
    lucas777閱讀 5,395評論 2 54
  • 不足的地方請大家多多指正,如有其它沒有想到的常問面試題請大家多多評論,一起成長,感謝!~ String可以被繼承嗎...
    啟示錄是真的閱讀 3,081評論 3 3
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,689評論 1 32
  • 聊聊定時任務 定時任務,顧名思義就是,定時去完成某項任務,例如指定某個時間點去做某件事情或者是指定一定的時間間隔去...
    義焃閱讀 2,187評論 0 2
  • 孤獨了一冬的情感 春風把它點燃 嬌艷盛開的桃花 在陽光下燦爛 但愿阡陌已久的心 能留下故事 用愛來豐盈整個春天 春...
    琢玉書生閱讀 1,141評論 9 42

友情鏈接更多精彩內(nèi)容