# 10.5 AlarmManager(鬧鐘服務)
## 本節引言:
> 本節帶來的Android中的AlarmManager(鬧鐘服務),聽名字我們知道可以通過它開發手機鬧鐘類的APP, 而在文檔中的解釋是:在特定的時刻為我們廣播一個指定的Intent,簡單說就是我們自己定一個時間, 然后當到時間時,AlarmManager會為我們廣播一個我們設定好的Intent,比如時間到了,可以指向某個 Activity或者Service!另外官方文檔中有一些要注意的地方:
>
> 
>
> 另外要注意一點的是,AlarmManager主要是用來在某個時刻運行你的代碼的,即時你的APP在那個特定 時間并沒有運行!還有,從API 19開始,Alarm的機制都是非準確傳遞的,操作系統將會轉換鬧鐘 ,來最小化喚醒和電池的使用!某些新的API會支持嚴格準確的傳遞,見 setWindow(int, long, long, PendingIntent)和setExact(int, long, PendingIntent)。 targetSdkVersion在API 19之前應用仍將繼續使用以前的行為,所有的鬧鐘在要求準確傳遞的情況 下都會準確傳遞。更多詳情可見官方API文檔:[AlarmManager](http://androiddoc.qiniudn.com/reference/android/app/AlarmManager.html)
## 1.Timer類與AlarmManager類區別:
> 如果你學過J2SE的話,那么你對Timer肯定不會陌生,定時器嘛,一般寫定時任務的時候 肯定離不開他,但是在Android里,他卻有個短板,不太適合那些需要長時間在后臺運行的 定時任務,因為Android設備有自己的休眠策略,當長時間的無操作,設備會自動讓CPU進入 休眠狀態,這樣就可能導致Timer中的定時任務無法正常運行!而AlarmManager則不存在 這種情況,因為他具有喚醒CPU的功能,可以保證每次需要執行特定任務時CPU都能正常工作, 或者說當CPU處于休眠時注冊的鬧鐘會被保留(可以喚醒CPU),但如果設備被關閉,或者重新 啟動的話,鬧鐘將被清除!(Android手機關機鬧鐘不響...)
## 2.獲得AlarmManager實例對象:
```
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
```
## 3.相關方法講解:
> * **set**(int type,long startTime,PendingIntent pi):一次性鬧鐘
> * **setRepeating**(int type,long startTime,long intervalTime,PendingIntent pi): 重復性鬧鐘,和3有區別,3鬧鐘間隔時間不固定
> * **setInexactRepeating**(int type,long startTime,long intervalTime,PendingIntent pi): 重復性鬧鐘,時間不固定
> * **cancel**(PendingIntent pi):取消AlarmManager的定時服務
> * **getNextAlarmClock**():得到下一個鬧鐘,返回值AlarmManager.AlarmClockInfo
> * **setAndAllowWhileIdle**(int type, long triggerAtMillis, PendingIntent operation) 和set方法類似,這個鬧鐘運行在系統處于低電模式時有效
> * **setExact**(int type, long triggerAtMillis, PendingIntent operation): 在規定的時間精確的執行鬧鐘,比set方法設置的精度更高
> * **setTime**(long millis):設置系統墻上的時間
> * **setTimeZone**(String timeZone):設置系統持續的默認時區
> * **setWindow**(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation): 設置一個鬧鐘在給定的時間窗觸發。類似于set,該方法允許應用程序精確地控制操作系統調 整鬧鐘觸發時間的程度。
**關鍵參數講解**:
> * **Type**(鬧鐘類型): 有五個可選值: AlarmManager.**ELAPSED_REALTIME**: 鬧鐘在手機睡眠狀態下不可用,該狀態下鬧鐘使用相對時間(相對于系統啟動開始),狀態值為3; AlarmManager.**ELAPSED_REALTIME_WAKEUP** 鬧鐘在睡眠狀態下會喚醒系統并執行提示功能,該狀態下鬧鐘也使用相對時間,狀態值為2; AlarmManager.**RTC** 鬧鐘在睡眠狀態下不可用,該狀態下鬧鐘使用絕對時間,即當前系統時間,狀態值為1; AlarmManager.**RTC_WAKEUP** 表示鬧鐘在睡眠狀態下會喚醒系統并執行提示功能,該狀態下鬧鐘使用絕對時間,狀態值為0; AlarmManager.**POWER_OFF_WAKEUP** 表示鬧鐘在手機關機狀態下也能正常進行提示功能,所以是5個狀態中用的最多的狀態之一,該狀態下鬧鐘也是用絕對時間,狀態值為4;不過本狀態好像受SDK版本影響,某些版本并不支持;
> * **startTime**:鬧鐘的第一次執行時間,以毫秒為單位,可以自定義時間,不過一般使用當前時間。 需要注意的是,本屬性與第一個屬性(type)密切相關,如果第一個參數對應的鬧鐘使用的是相對時間 (ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那么本屬性就得使用相對時間 (相對于系統啟動時間來說),比如當前時間就表示為:SystemClock.elapsedRealtime(); 如果第一個參數對應的鬧鐘使用的是絕對時間(RTC、RTC_WAKEUP、POWER_OFF_WAKEUP), 那么本屬性就得使用絕對時間,比如當前時間就表示 為:System.currentTimeMillis()。
> * **intervalTime**:表示兩次鬧鐘執行的間隔時間,也是以毫秒為單位.
> * **PendingIntent**:綁定了鬧鐘的執行動作,比如發送一個廣播、給出提示等等。 PendingIntent是Intent的封裝類。需要注意的是,如果是通過啟動服務來實現鬧鐘提 示的話,PendingIntent對象的獲取就應該采用Pending.getService (Context c,int i,Intent intent,int j)方法;如果是通過廣播來實現鬧鐘 提示的話,PendingIntent對象的獲取就應該采用 PendingIntent.getBroadcast (Context c,int i,Intent intent,int j)方法;如果是采用Activity的方式來實 現鬧鐘提示的話,PendingIntent對象的獲取就應該采用 PendingIntent.getActivity(Context c,int i,Intent intent,int j)方法。 如果這三種方法錯用了的話,雖然不會報錯,但是看不到鬧鐘提示效果。
## 4.使用示例:一個簡單的定時任務
> 要說的是,此例子只在Android 4.4以下的系統可行,5.0以上并不可行,后續如果有5.0 以上AlarmManager的解決方案,到時再補上!另外,這里用set方法可能有點不準,如果要 更精確的話可以使用setExtra()方法來設置AlarmManager!
**運行效果圖**:
 
**實現代碼**:
首先一個簡單的布局文件:**activity_main.xml**,另外在res創建一個raw文件夾,把音頻文件丟進去! 另外創建一個只有外層布局的**activity_clock.xml**作為鬧鐘響時Activity的布局!沒東西,就不貼了
```
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn_set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="設置鬧鐘" />
<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="關閉鬧鐘"
android:visibility="gone" />
</LinearLayout>
```
接著是**MainActivity.java**,也很簡單:
```
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button btn_set;
private Button btn_cancel;
private AlarmManager alarmManager;
private PendingIntent pi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindViews();
}
private void bindViews() {
btn_set = (Button) findViewById(R.id.btn_set);
btn_cancel = (Button) findViewById(R.id.btn_cancel);
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent(MainActivity.this, ClockActivity.class);
pi = PendingIntent.getActivity(MainActivity.this, 0, intent, 0);
btn_set.setOnClickListener(this);
btn_cancel.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_set:
Calendar currentTime = Calendar.getInstance();
new TimePickerDialog(MainActivity.this, 0,
new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view,
int hourOfDay, int minute) {
//設置當前時間
Calendar c = Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis());
// 根據用戶選擇的時間來設置Calendar對象
c.set(Calendar.HOUR, hourOfDay);
c.set(Calendar.MINUTE, minute);
// ②設置AlarmManager在Calendar對應的時間啟動Activity
alarmManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
Log.e("HEHE",c.getTimeInMillis()+""); //這里的時間是一個unix時間戳
// 提示鬧鐘設置完畢:
Toast.makeText(MainActivity.this, "鬧鐘設置完畢~"+ c.getTimeInMillis(),
Toast.LENGTH_SHORT).show();
}
}, currentTime.get(Calendar.HOUR_OF_DAY), currentTime
.get(Calendar.MINUTE), false).show();
btn_cancel.setVisibility(View.VISIBLE);
break;
case R.id.btn_cancel:
alarmManager.cancel(pi);
btn_cancel.setVisibility(View.GONE);
Toast.makeText(MainActivity.this, "鬧鐘已取消", Toast.LENGTH_SHORT)
.show();
break;
}
}
}
```
然后是鬧鈴頁面的**ClockActivity.java**:
```
/**
* Created by Jay on 2015/10/25 0025.
*/
public class ClockActivity extends AppCompatActivity {
private MediaPlayer mediaPlayer;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clock);
mediaPlayer = mediaPlayer.create(this,R.raw.pig);
mediaPlayer.start();
//創建一個鬧鐘提醒的對話框,點擊確定關閉鈴聲與頁面
new AlertDialog.Builder(ClockActivity.this).setTitle("鬧鐘").setMessage("小豬小豬快起床~")
.setPositiveButton("關閉鬧鈴", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mediaPlayer.stop();
ClockActivity.this.finish();
}
}).show();
}
}
```
代碼非常簡單,核心流程如下:
> * AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); **獲得系統提供的AlarmManager服務的對象**
> * **Intent設置要啟動的組件**: Intent intent = new Intent(MainActivity.this, ClockActivity.class);
> * **PendingIntent對象設置動作,啟動的是Activity還是Service,又或者是廣播!** PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0, intent, 0);
> * **調用AlarmManager的set( )方法設置單次鬧鐘的鬧鐘類型,啟動時間以及PendingIntent對象!** alarmManager.set(AlarmManager.RTC_WAKEUP,c.getTimeInMillis(), pi);
另外假如出現鬧鈴無效的話,你可以從這些方面入手:
> 1.系統版本或者手機,5.0以上基本沒戲,小米,自行百度吧~ 2.ClockActivity有注冊沒? 3.假如你用的是alarmManager發送廣播,廣播再激活Activity的話,則需要為Intent設置一個flag: i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 4\.  這些地方沒寫錯吧~別把getActivity寫成了getService等哦~
另外,關于AlarmManager結合后來Service實現定時后臺任務的例子,可見: [4.2.2 Service進階](http://www.runoob.com/w3cnote/android-tutorial-service-2.html "4.2.2 Service進階")
## 5.代碼示例下載:
[AlarmManagerDemo.zip](http://static.runoob.com/download/AlarmManagerDemo.zip)
## 本節小結:
> 好的,本節跟大家講解了Android中的AlarmManager(鬧鐘服務)的使用,除了可以像例子那樣定制 一個自己的鬧鐘,也可以結合Service,Thread來完成輪詢等,用法多多,還需各位自行探究,嗯 本節就到這里,謝謝~
- 1.0 Android基礎入門教程
- 1.0.1 2015年最新Android基礎入門教程目錄
- 1.1 背景相關與系統架構分析
- 1.2 開發環境搭建
- 1.2.1 使用Eclipse + ADT + SDK開發Android APP
- 1.2.2 使用Android Studio開發Android APP
- 1.3 SDK更新不了問題解決
- 1.4 Genymotion模擬器安裝
- 1.5.1 Git使用教程之本地倉庫的基本操作
- 1.5.2 Git之使用GitHub搭建遠程倉庫
- 1.6 .9(九妹)圖片怎么玩
- 1.7 界面原型設計
- 1.8 工程相關解析(各種文件,資源訪問)
- 1.9 Android程序簽名打包
- 1.11 反編譯APK獲取代碼&資源
- 2.1 View與ViewGroup的概念
- 2.2.1 LinearLayout(線性布局)
- 2.2.2 RelativeLayout(相對布局)
- 2.2.3 TableLayout(表格布局)
- 2.2.4 FrameLayout(幀布局)
- 2.2.5 GridLayout(網格布局)
- 2.2.6 AbsoluteLayout(絕對布局)
- 2.3.1 TextView(文本框)詳解
- 2.3.2 EditText(輸入框)詳解
- 2.3.3 Button(按鈕)與ImageButton(圖像按鈕)
- 2.3.4 ImageView(圖像視圖)
- 2.3.5.RadioButton(單選按鈕)&Checkbox(復選框)
- 2.3.6 開關按鈕ToggleButton和開關Switch
- 2.3.7 ProgressBar(進度條)
- 2.3.8 SeekBar(拖動條)
- 2.3.9 RatingBar(星級評分條)
- 2.4.1 ScrollView(滾動條)
- 2.4.2 Date & Time組件(上)
- 2.4.3 Date & Time組件(下)
- 2.4.4 Adapter基礎講解
- 2.4.5 ListView簡單實用
- 2.4.6 BaseAdapter優化
- 2.4.7ListView的焦點問題
- 2.4.8 ListView之checkbox錯位問題解決
- 2.4.9 ListView的數據更新問題
- 2.5.0 構建一個可復用的自定義BaseAdapter
- 2.5.1 ListView Item多布局的實現
- 2.5.2 GridView(網格視圖)的基本使用
- 2.5.3 Spinner(列表選項框)的基本使用
- 2.5.4 AutoCompleteTextView(自動完成文本框)的基本使用
- 2.5.5 ExpandableListView(可折疊列表)的基本使用
- 2.5.6 ViewFlipper(翻轉視圖)的基本使用
- 2.5.7 Toast(吐司)的基本使用
- 2.5.8 Notification(狀態欄通知)詳解
- 2.5.9 AlertDialog(對話框)詳解
- 2.6.0 其他幾種常用對話框基本使用
- 2.6.1 PopupWindow(懸浮框)的基本使用
- 2.6.2 菜單(Menu)
- 2.6.3 ViewPager的簡單使用
- 2.6.4 DrawerLayout(官方側滑菜單)的簡單使用
- 3.1.1 基于監聽的事件處理機制
- 3.2 基于回調的事件處理機制
- 3.3 Handler消息傳遞機制淺析
- 3.4 TouchListener PK OnTouchEvent + 多點觸碰
- 3.5 監聽EditText的內容變化
- 3.6 響應系統設置的事件(Configuration類)
- 3.7 AnsyncTask異步任務
- 3.8 Gestures(手勢)
- 4.1.1 Activity初學乍練
- 4.1.2 Activity初窺門徑
- 4.1.3 Activity登堂入室
- 4.2.1 Service初涉
- 4.2.2 Service進階
- 4.2.3 Service精通
- 4.3.1 BroadcastReceiver牛刀小試
- 4.3.2 BroadcastReceiver庖丁解牛
- 4.4.2 ContentProvider再探——Document Provider
- 4.5.1 Intent的基本使用
- 4.5.2 Intent之復雜數據的傳遞
- 5.1 Fragment基本概述
- 5.2.1 Fragment實例精講——底部導航欄的實現(方法1)
- 5.2.2 Fragment實例精講——底部導航欄的實現(方法2)
- 5.2.3 Fragment實例精講——底部導航欄的實現(方法3)
- 5.2.4 Fragment實例精講——底部導航欄+ViewPager滑動切換頁面
- 5.2.5 Fragment實例精講——新聞(購物)類App列表Fragment的簡單實現
- 6.1 數據存儲與訪問之——文件存儲讀寫
- 6.2 數據存儲與訪問之——SharedPreferences保存用戶偏好參數
- 6.3.1 數據存儲與訪問之——初見SQLite數據庫
- 6.3.2 數據存儲與訪問之——又見SQLite數據庫
- 7.1.1 Android網絡編程要學的東西與Http協議學習
- 7.1.2 Android Http請求頭與響應頭的學習
- 7.1.3 Android HTTP請求方式:HttpURLConnection
- 7.1.4 Android HTTP請求方式:HttpClient
- 7.2.1 Android XML數據解析
- 7.2.2 Android JSON數據解析
- 7.3.1 Android 文件上傳
- 7.3.2 Android 文件下載(1)
- 7.3.3 Android 文件下載(2)
- 7.4 Android 調用 WebService
- 7.5.1 WebView(網頁視圖)基本用法
- 7.5.2 WebView和JavaScrip交互基礎
- 7.5.3 Android 4.4后WebView的一些注意事項
- 7.5.4 WebView文件下載
- 7.5.5 WebView緩存問題
- 7.5.6 WebView處理網頁返回的錯誤碼信息
- 7.6.1 Socket學習網絡基礎準備
- 7.6.2 基于TCP協議的Socket通信(1)
- 7.6.3 基于TCP協議的Socket通信(2)
- 7.6.4 基于UDP協議的Socket通信
- 8.1.1 Android中的13種Drawable小結 Part 1
- 8.1.2 Android中的13種Drawable小結 Part 2
- 8.1.3 Android中的13種Drawable小結 Part 3
- 8.2.1 Bitmap(位圖)全解析 Part 1
- 8.2.2 Bitmap引起的OOM問題
- 8.3.1 三個繪圖工具類詳解
- 8.3.2 繪圖類實戰示例
- 8.3.3 Paint API之—— MaskFilter(面具)
- 8.3.4 Paint API之—— Xfermode與PorterDuff詳解(一)
- 8.3.5 Paint API之—— Xfermode與PorterDuff詳解(二)
- 8.3.6 Paint API之—— Xfermode與PorterDuff詳解(三)
- 8.3.7 Paint API之—— Xfermode與PorterDuff詳解(四)
- 8.3.8 Paint API之—— Xfermode與PorterDuff詳解(五)
- 8.3.9 Paint API之—— ColorFilter(顏色過濾器)(1/3)
- 8.3.10 Paint API之—— ColorFilter(顏色過濾器)(2-3)
- 8.3.11 Paint API之—— ColorFilter(顏色過濾器)(3-3)
- 8.3.12 Paint API之—— PathEffect(路徑效果)
- 8.3.13 Paint API之—— Shader(圖像渲染)
- 8.3.14 Paint幾個枚舉/常量值以及ShadowLayer陰影效果
- 8.3.15 Paint API之——Typeface(字型)
- 8.3.16 Canvas API詳解(Part 1)
- 8.3.17 Canvas API詳解(Part 2)剪切方法合集
- 8.3.18 Canvas API詳解(Part 3)Matrix和drawBitmapMash
- 8.4.1 Android動畫合集之幀動畫
- 8.4.2 Android動畫合集之補間動畫
- 8.4.3 Android動畫合集之屬性動畫-初見
- 8.4.4 Android動畫合集之屬性動畫-又見
- 9.1 使用SoundPool播放音效(Duang~)
- 9.2 MediaPlayer播放音頻與視頻
- 9.3 使用Camera拍照
- 9.4 使用MediaRecord錄音
- 10.1 TelephonyManager(電話管理器)
- 10.2 SmsManager(短信管理器)
- 10.3 AudioManager(音頻管理器)
- 10.4 Vibrator(振動器)
- 10.5 AlarmManager(鬧鐘服務)
- 10.6 PowerManager(電源服務)
- 10.7 WindowManager(窗口管理服務)
- 10.8 LayoutInflater(布局服務)
- 10.9 WallpaperManager(壁紙管理器)
- 10.10 傳感器專題(1)——相關介紹
- 10.11 傳感器專題(2)——方向傳感器
- 10.12 傳感器專題(3)——加速度/陀螺儀傳感器
- 10.12 傳感器專題(4)——其他傳感器了解
- 10.14 Android GPS初涉
- 11.0《2015最新Android基礎入門教程》完結散花~