Android 警報 (Alarm) 及通知 (Notification)

Notification Example

在 Android 中可以使用警報(Alarm)及通知(Notification)的搭配來提醒使用者。

警報(Alarm)

Alarm 可以在預定的時間某個時間間隔觸發意圖來提醒使用者。Alarm 是在應用程式之外設定的,也就是即使啟動它的 App 關閉了,仍會在指定的時間到達時發出警報。

Alarm 也可使用時間間隔來發出警報,例如每隔 10 分鐘、每天早上 8 點等等。當 Android 裝置(手機)在休眠狀態(螢幕關閉)下,有指定喚醒(螢幕亮起)裝置的 Alarm 將會喚醒裝置並發出警報,例如鬧鐘程式會在指定時間到達時喚醒裝置並發出警報。

有個必須要注意的地方,當 Android 裝置(手機)重新啟動後,所有指定的 Alarm 都會被取消,必須利用其他方法在重開機後將 Alarm 重新設定。

通知(Notification)

Notification 是應用程式提醒使用者的一種方式,它會利用通知管理器進行處理而不需要 Activity 。通知的方式有幾種:建立狀態欄圖示、閃燈 (LED)、振動、鈴聲。

Notification 通常會被用在 Broadcast Receiver 或 Service 等不可見的應用程式中,用來提醒使用者執行狀態或結果。如果把 Alarm 結合 Notification 就可以在時間到達時發出通知來提醒使用。

AlarmManager 的使用

要使用 Alarm 可以透過 AlarmManager 來管理 Alarm。AlarmManager 是一個系統服務 (System Service),取得方法如下:
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
要設定一個警報只要使用 set 方法,同時指定一個警報類型觸發時間及一個待處理意圖。如果這個觸發時間是在過去,那這個警報會被立即觸發。

警報類型有 4 種:
  • RTC_WAKEUP:在指定時間觸發意圖並喚醒裝置。
  • RTC:同上但不喚醒裝置。
  • ELAPSED_REALTIME:在裝置啟動(開機)後開始計算經過的時間,在到達指定的經過時間觸發意圖,但不喚醒裝置。
  • ELAPSED_REALTIME_WAKEUP:同上,但會喚醒裝置。
設定一個警報的範例:
//使用Calendar指定時間
Calendar calendar = Calendar.getInstance();
calendar.set(2012, 10, 8, 16, 30);

//建立意圖
Intent intent = new Intent();

//這裡的 this 是指當前的 Activity
//AlarmReceiver.class 則是負責接收的 BroadcastReceiver
intent.setClass(this, AlarmReceiver.class);

//建立待處理意圖
PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, 0);

//取得AlarmManager
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

//設定一個警報
//參數1,我們選擇一個會在指定時間喚醒裝置的警報類型
//參數2,將指定的時間以millisecond傳入
//參數3,傳入待處理意圖
alarm.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pending);
如果你用相同的待處理意圖(PendingIntent)設定第 2 個警報,那第 2 個警報將會取代原有的警報。

那要如何取消警報呢?同樣以待處理意圖為目標,使用 cancel 方法將其傳入,即可取消該警報。如下:
alarm.cancel(pending);

重覆性警報(Repeating Alarm)

相對於一次性警報,重覆性警報會在一定時間內重覆執行,直到被取消。要設定重覆性警報有 2 種方法,setRepeating() 或 setInexactRepeating() ,兩者的差別在於 setRepeating() 可以以精確到毫秒的時間間隔來執行,當然就會比較耗電,使用 setInexactRepeating() 則可以避免耗電。

setInexactRepeating() 可以使用較不精確的時間間隔來設定,而這些在 AlarmManager 類別中已有內定的常數可使用,如下:
  • INTERVAL_FIFTEEN_MINUTES
  • INTERVAL_HALF_HOUR
  • INTERVAL_HOUR
  • INTERVAL_HALF_DAY
  • INTERVAL_DAY
很簡單易懂的常數值,每隔 15 分鐘、半小時、1 小時、半天 (12小時) 及 1 天 (24小時) 的時間間隔發出警報。

如果設定的多個非精確 (setInexactRepeating) 時間間隔,時間雖未重疊但很接近,Android 會同時觸發它們,避免不斷地發佈警報,在 API 中的解釋為 phase-aligned (相位對齊)。使用範例如下:

精確

alarm.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 10 * 1000, 10 * 1000, pending);

非精確

alarm.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 60 * 1000, AlarmManager.INTERVAL_FIFTEEN_MINUTES, pending);

接收警報

當 alarm 被觸發時,需要一個 receiver 來接收它,然後指示要做什麼事,所以我們要繼承一個 BroadcastReceiver 並實作它的 onReceive 方法。如下:
public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        //....do something
    }
}
你可以在此發出通知告知使用者,或執行資料更新的動作等等。

NotificationManager 的使用

NotificationManager 和 AlarmManager 一樣是系統級的服務,所以取得方法一樣,如下:
NotificationManager noMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
完整的通知範例:
//取得通知管理器
NotificationManager noMgr = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);

//當使用者按下通知欄中的通知時要開啟的 Activity
Intent call = new Intent(this, AlarmDemoActivity.class);
//非必要,可以利用intent傳值
call.putExtra("notiId", 1);
//建立待處理意圖
PendingIntent pIntent = PendingIntent.getActivity(this, 0, call, 0); 

//指定通知欄位要顯示的圖示
int icon = R.drawable.ic_launcher;
//指定通知出現時要顯示的文字,幾秒後會消失只剩圖示
String ticket = "電影時刻通知";
//何時送出通知,傳入當前時間則立即發出通知
long when = System.currentTimeMillis();
//建立通知物件
Notification notification = new Notification(icon, ticket, when);

//指定通知標題
String title = "鐵達尼號3D";
//通知內容
String desc = "播放時間17:00 - 新光影城";
//設定事件資訊
notification.setLatestEventInfo(this, title, desc, pIntent);

//非必要,會在通知圖示旁顯示數字
notification.number = 5;

//執行通知
noMgr.notify(1, notification);
收到通知時顯示:
幾秒後文字消失,顯示數字: 如果要消除數字,可以將其設為 0 或 -1,若一開始就沒指定則不會出現。

這邊要記住,只要送出的通知是同一個 PendingIntent ,那就不會產生新的通知,而是覆蓋重覆的通知。你可以建立一個變數,然後在每次發出通知後就加1,只要一直發佈重覆的通知,就只有數字會改變,不會跑出好幾個通知。

通知的內容: 如果你要使用自訂的通知佈局,可以使用 RemoteView 來實作,並指定給 notification.contentView ,如下:
notification.contentView = new RemoteViews(this.getPackageName(), R.layout.notification);
並且要同時把 PendingIntent 指定給 contentIntent 屬性,否則會出錯,如下:
notification.contentIntent = pIntent;

警報的補充

記得當你實作 BroadcastReceiver 時,在 AndroidManifest.xml 要加入你的 Receiver ,如下:
<receiver android:name="AlarmReceiver"></receiver>
AlarmReceiver 是你的 Receiver 類別的名稱。

此外,在一開始介紹 Alarm 時有說過,當裝置(手機)重新開機後,所有的 Alarm 都將被取消,那該怎麼解決呢?一樣利用 Receiver,我們可以如同建立 AlarmReceiver 一樣,建立一個 AlarmInitReceiver (繼承 BroadcastReceiver 並實作 onReceive),接著在 AndroidManifest.xml 中加入這個 Receiver 來接收開機訊息,如下:
<receiver android:name="AlarmInitReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>
BOOT_COMPLETED 是 Android 內建的 action,會在開機後送出廣播,所以我們建立一個接收者去接收這個廣播即可。使用這個 action 必須增加使用權限,如下:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
如此一來就完成了開機廣播的接收,當你收到這個廣播後,就可以重新設定你的 Alarm ,不會因為使用者重開機而遺失了警報。
本文網址:https://blog.tonycube.com/2012/10/alarmnotification.html
Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀

6 則留言

  1. 不好意思 請問一下 這個可以用來做每兩個月的發票兌獎的提醒嗎?
    還有 如果要用在Eclipse寫 程式碼要放在那裡呢?

    回覆刪除
    回覆
    1. 這個功能不是在行事曆裡面自己加提醒就可以了。

      刪除
  2. 請問這個有範例可以下載嗎

    回覆刪除
    回覆
    1. 這篇好久以前寫的,找不到了~

      刪除
  3. 不好意思,現在還會回覆嗎?我想請問如何建立多個setRepeating的警報,且每個警報都做同樣的發送訊息動作?
    且現在android 改版,我制定的鬧鐘時間會在滅屏後延遲發佈訊息,怎麼辦?

    回覆刪除
    回覆
    1. 太久沒用了,我查了一下資料,不確定可不可行,
      可以試試看建立多個 Alarm ,每個 Alarm 有自己的 Repeating 警報,
      而不是一個 Alram 多個 Repeating 警報

      另外,查API文件
      https://developer.android.com/reference/android/app/AlarmManager
      找到 setAndAllowWhileIdle() 或 setExactAndAllowWhileIdle()
      這兩個方法,看說明可能符合你的要求,可以試看看

      刪除

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。