Android定時任務(wù)

本章目錄

  • Part One:Timer
  • Part Two:AlarmManager

Android中有很多種實(shí)現(xiàn)定時任務(wù)的方式,比如Timer,CountDownTimer, AlarmManager,handler和Thread。不過,主要常用的有三種:

  1. Timer(Java遺留的)
  2. Handler(下雪動畫那篇使用過了)
  3. AlarmManager(Android官方推薦)

Part One:Timer

Timer是一個定時器工具,包含一系列的schedule方法用于實(shí)施定時計劃。TimerTask是一個子線程的抽象類,方便在后臺處理一些比較復(fù)雜的邏輯,然后利用Handler在主線程刷新UI。
下面我們通過修改先前的案例來認(rèn)識一下這兩個類的具體應(yīng)用。

  • 首先,在activity_main.xml的cacheContainer_main布局容器中,放一個TextView,用來顯示滾動文字。
        <TextView
            android:id="@+id/textView_main"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:textColor="@android:color/white"
            android:layout_marginBottom="64dp"/>
  • 然后在MainActivity里面初始化這個TextView和SnowView(這里初始化SnowView是用來關(guān)閉自定義View的繪制)。
    private TextView textView;
    private SnowView snowView;
    
    private void initViews() {
        cacheContainer = findViewById(R.id.cacheContainer_main);
        animContainer = findViewById(R.id.animContainer_main);
        leftAnimImageView = findViewById(R.id.leftAnimContainer_main);
        rightAnimImageView = findViewById(R.id.rightAnimContainer_main);
        textView = findViewById(R.id.textView_main);
        snowView = findViewById(R.id.snowView_main);
    }
  • 接著初始化一段文字?jǐn)?shù)組,用來以滾動的方式,不斷更新顯示的文字。
    private String[] contents = new String[]{"時間久了,我都快忘記我們相識多少年了,",
            "我只知道我認(rèn)識你很久了,", "漫長的時間卻拉不近我們的距離,",
            "一天一點(diǎn)愛戀,一夜一點(diǎn)思念,", "不想再等了,有些話想對你說。"};
  • 最后,把先前定義的openWindow方法改成switchContent,讓方法名字表達(dá)的更準(zhǔn)確一些。同時在方法里初始化Timer和TimerTask,并調(diào)用相應(yīng)的方法。
    private int index = 0;//數(shù)組的索引,用于讓TextView顯示不同內(nèi)容,初識從0開始
    private void switchContent() {
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                //刷新UI必須執(zhí)行在主線程
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //如果字符串?dāng)?shù)組全部顯示完畢,下標(biāo)清0,防止越界。同是,讓自定義View停止繪制。
                        //Timer停止計劃任務(wù),并執(zhí)行開窗動畫
                        if (index == contents.length){
                            index = 0;
                            snowView.stopDraw();
                            initCache();
                            timer.cancel();
                        }
                        //讓TextView滾動顯示文字
                        textView.setText(contents[index]);
                        index++;
                    }
                });
            }
        }, 50, 4000);
    }

整個計劃任務(wù)的實(shí)現(xiàn)邏輯并不復(fù)雜,就是不斷變換數(shù)組索引坐標(biāo),讓TextView顯示不同的內(nèi)容。
這里要重點(diǎn)說一下我們使用的timer.schedule(task, delay, period)的三個參數(shù):

  1. task:TimerTask對象,就是要周期執(zhí)行的任務(wù)。
  2. delay:從計時器初始化完畢后,開始啟動的延遲時間。
  3. period:定時器的間隔時間。
    比如,我們的案例就是50毫秒后啟動定時器,沒4秒更新一下文字,文字刷新完執(zhí)行動畫。


    滾動文字.gif

Part Two:AlarmManager

前面提到的Timer實(shí)現(xiàn)定時任務(wù)的方式使用的是Java的API,大部分情況下都是滿足需求的,比如本例。但是如果遇到手機(jī)鎖屏或者關(guān)機(jī),喚醒CPU執(zhí)行定時任務(wù)時,就會遇到一些問題,比如鬧鐘。所以,官方推薦使用AlrmManager來執(zhí)行定時任務(wù)。
AlarmManager的實(shí)現(xiàn)方式也不是很復(fù)雜,只是需要用到PendingIntent,具體步驟如下:

  • 先前的準(zhǔn)備工作不變,只是改變switchContent方法。首先,注釋掉先前寫的代碼,然后聲明一個全局的AlarmManager
    private void switchContent() {
        alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    }

    private AlarmManager alarmManager;
  • 然后聲明一個全局的PendingIntent,并設(shè)置AlarmManager的重復(fù)方式
    private void switchContent() {
        alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent intent = new Intent();
        intent.setAction("action.REFRESHTEXTVIEW");
        pendingIntent = PendingIntent.getBroadcast(this,
                100, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
                5000, 60000, pendingIntent);
    }

    private AlarmManager alarmManager;
    private PendingIntent pendingIntent;

其中,PendingIntent是個延遲意圖對象,這里是發(fā)送廣播,所以使用getBroadcast方法,也可以使用getService啟動Service或者getActivity啟動Activity來執(zhí)行某項固定任務(wù)。getBroadcast的參數(shù)前面都好理解,最后一個參數(shù)的效果有:

  1. FLAG_CANCEL_CURRENT:如果系統(tǒng)中有一個相同的PendingIntent對象,那么就取消舊的,然后重新生成一個。
  2. FLAG_NO_CREATE:如果當(dāng)前系統(tǒng)中不存在相同的PendingIntent對象,系統(tǒng)將不會創(chuàng)建該P(yáng)endingIntent對象而是直接返回null。
  3. FLAG_ONE_SHOT:該P(yáng)endingIntent只作用一次。在該P(yáng)endingIntent對象通過send()方法觸發(fā)過后,PendingIntent將自動調(diào)用cancel()進(jìn)行銷毀,那么如果你再調(diào)用send()方法的話,系統(tǒng)將會返回一個SendIntentException。
  4. FLAG_UPDATE_CURRENT:如果系統(tǒng)中有一個和你描述的PendingIntent對等的PendingInent,那么系統(tǒng)將使用該P(yáng)endingIntent對象,但是會使用新的Intent來更新之前PendingIntent中的Intent對象數(shù)據(jù),例如更新Intent中的Extras。
    另外,setRepeating也需要注意,從Android5.0開始,第二個參數(shù)triggerAtMillis(觸發(fā)時間)不得低于5秒,intervalMillis(重復(fù)間隔)不得低于60秒。這兩個參數(shù)如果比這兩個值小,默認(rèn)分別是5秒和60秒,只有大于才會生效,因為間隔太短會費(fèi)電~短時間的定時任務(wù),官方推薦使用Handler方式。
    所以,其實(shí)AlarmManager并不適合本例,這里只是講一下用法。
    setRepeating方法的第一個參數(shù)也單獨(dú)說下:
  5. AlarmManager.RTC_WAKEUP表示鬧鐘在睡眠狀態(tài)下會喚醒系統(tǒng)并執(zhí)行提示功能,該狀態(tài)下鬧鐘使用絕對時間,狀態(tài)值為0;
  6. AlarmManager.RTC表示鬧鐘在睡眠狀態(tài)下不可用,該狀態(tài)下鬧鐘使用絕對時間,即當(dāng)前系統(tǒng)時間,狀態(tài)值為1;
  7. AlarmManager.ELAPSED_REALTIME_WAKEUP表示鬧鐘在睡眠狀態(tài)下會喚醒系統(tǒng)并執(zhí)行提示功能,該狀態(tài)下鬧鐘也使用相對時間,狀態(tài)值為2;
  8. AlarmManager.ELAPSED_REALTIME表示鬧鐘在手機(jī)睡眠狀態(tài)下不可用,該狀態(tài)下鬧鐘使用相對時間(相對于系統(tǒng)啟動開始),狀態(tài)值為3;
  9. AlarmManager.POWER_OFF_WAKEUP表示鬧鐘在手機(jī)關(guān)機(jī)狀態(tài)下也能正常進(jìn)行提示功能,所以是5個狀態(tài)中用的最多的狀態(tài)之一,該狀態(tài)下鬧鐘也是用絕對時間,狀態(tài)值為4;不過本狀態(tài)好像受SDK版本影響,某些版本并不支持;
  • 接下來就是自定義一個廣播接收者,當(dāng)接收到廣播時,執(zhí)行的任務(wù):
    private class AlarmBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (index == contents.length) {
                index = 0;
                snowView.stopDraw();
                initCache();
                alarmManager.cancel(pendingIntent);//取消定時器
            }
            //讓TextView滾動顯示文字
            textView.setText(contents[index]);
            index++;
        }
    }
  • 最后,將廣播注冊即可
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("action.REFRESHTEXTVIEW");
        BroadcastReceiver receiver = new AlarmBroadcastReceiver();
        registerReceiver(receiver, intentFilter);

結(jié)果就是每隔60秒發(fā)送一個廣播,廣播接收器收到廣播后,執(zhí)行相應(yīng)的任務(wù)。

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

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

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