項目需要:實現一個定時提醒的功能
查閱資料知道,需要使用AlarmManager
AlarmManager介紹:
AlarmManager是Android中常用的一種系統級別的提示服務,在特定的時刻為我們廣播一個指定的Intent。簡單的說就是我們設定一個時間,然后在該時間到來時,AlarmManager為我們廣播一個我們設定的Intent,通常我們使用 PendingIntent,PendingIntent可以理解為Intent的封裝包,簡單的說就是在Intent上在加個指定的動作。在使用Intent的時候,我們還需要在執行startActivity、startService或sendBroadcast才能使Intent有用。而PendingIntent的話就是將這個動作包含在內了。
定義一個PendingIntent對象。
PendingIntent pi = PendingIntent.getBroadcast(this,0,intent,0);
2、AlarmManager的常用方法有三個:
(1)set(int type,long startTime,PendingIntent pi);
該方法用於設置一次性鬧鍾,第一個參數表示鬧鍾類型,第二個參數表示鬧鍾執行時間,第三個參數表示鬧鍾響應動作。
(2)setRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
該方法用於設置重復鬧鍾,第一個參數表示鬧鍾類型,第二個參數表示鬧鍾首次執行時間,第三個參數表示鬧鍾兩次執行的間隔時間,第三個參數表示鬧鍾響應動作。
(3)setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi);
該方法也用於設置重復鬧鍾,與第二個方法相似,不過其兩個鬧鍾執行的間隔時間不是固定的而已。
3、三個方法各個參數詳悉:
(1)int type: 鬧鍾的類型,常用的有5個值:AlarmManager.ELAPSED_REALTIME、 AlarmManager.ELAPSED_REALTIME_WAKEUP、AlarmManager.RTC、 AlarmManager.RTC_WAKEUP、AlarmManager.POWER_OFF_WAKEUP。
AlarmManager.ELAPSED_REALTIME表示鬧鍾在手機睡眠狀態下不可用,該狀態下鬧鍾使用相對時間(相對於系統啟動開始),狀態值為3;
AlarmManager.ELAPSED_REALTIME_WAKEUP表示鬧鍾在睡眠狀態下會喚醒系統並執行提示功能,該狀態下鬧鍾也使用相對時間,狀態值為2;
AlarmManager.RTC表示鬧鍾在睡眠狀態下不可用,該狀態下鬧鍾使用絕對時間,即當前系統時間,狀態值為1;
AlarmManager.RTC_WAKEUP表示鬧鍾在睡眠狀態下會喚醒系統並執行提示功能,該狀態下鬧鍾使用絕對時間,狀態值為0;
AlarmManager.POWER_OFF_WAKEUP表示鬧鍾在手機關機狀態下也能正常進行提示功能,所以是5個狀態中用的最多的狀態之一,該狀態下鬧鍾也是用絕對時間,狀態值為4;不過本狀態好像受SDK版本影響,某些版本並不支持;
(2)long startTime: 鬧鍾的第一次執行時間,以毫秒為單位,可以自定義時間,不過一般使用當前時間。需要注意的是,本屬性與第一個屬性(type)密切相關,(3)long intervalTime:對於后兩個方法來說,存在本屬性,表示兩次鬧鍾執行的間隔時間,也是以毫秒為單位。
(4)PendingIntent pi: 綁定了鬧鍾的執行動作,比如發送一個廣播、給出提示等等。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)方法。如果這三種方法錯用了的話,雖然不會報錯,但是看不到鬧鍾提示效果。
屬性或方法名稱 | 說明 |
ELAPSED_REALTIME | 設置鬧鍾時間,從系統啟動開 始 |
ELAPSED_REALTIME_WAKEUP | 同上,如果設備休眠則喚醒 |
INTERVAL_DAY | 設置鬧鍾,間隔一天 |
INTERVAL_HALF_DAY | 設置鬧鍾,間隔半天 |
INTERVAL_FIFTEEN_MINUTES | 設置鬧鍾,間隔15分鍾 |
INTERVAL_HALF_HOUR | 設置鬧鍾,間隔半個小時 |
INTERVAL_HOUR | 設置鬧鍾,間隔一個小時 |
RTC | 設置鬧鍾時間從系統當前時間開(System.currentTimeMillis()) |
RTC_WAKEUP | 同上,設備休眠則喚醒 |
set(int type,long triggerAtTime,PendingIntent operation) | 設置在某個時間執行鬧鍾 |
setRepeating(int type,long triggerAtTime,long interval PendingIntent operation) | 設置在某個時間重復執行鬧鍾 |
setInexactRepeating(int type,long triggerAtTime,long interval PendingIntent operation) | 設置在某個時間重復執行鬧鍾,但不是間隔固定時間 |
注意:
- 設置鬧鍾使用AlarmManager.set()函數,它的triggerAtTime參數,如果要用Calendar.getTimesInMillis()獲得,就必須先設置Calendar對象,例如要讓鬧鍾在當天的16:30分啟動,就要設置HOUR_OF_DAY(16)、MINUTE(30)、MILLISECOND(0),特別是HOUR_OF_DAY,我一開始誤用了HOUR,這是12進制計時方法,HOUR_OF_DAY是24進制計時方法。
- 針對同一個PendingIntent,AlarmManager.set()函數不能設置多個alarm。調用該函數時,假如已經有old alarm使用相同的PendingIntent,會先取消(cancel)old alarm,然后再設置新的alarm。如何判斷是否已經有相同的PendingIntent,請看下條。
- 取消alarm使用AlarmManager.cancel()函數,傳入參數是個PendingIntent實例。該函數會將所有跟這個PendingIntent相同的Alarm全部取消,怎么判斷兩者是否相同,android使用的是intent.filterEquals(),具體就是判斷兩個PendingIntent的action、data、type、class和category是否完全相同。
- 在AndroidManifest.xml中靜態注冊BroadcastReceiver時,一定使用android:process=":xxx"屬性,因為SDK已注明:If the name assigned to this attribute begins with a colon (':'), a new process, private to the application, is created when it's needed and the broadcast receiver runs in that process.
-
在此討論一下process屬性,它規定了組件(activity, service, receiver等)所在的進程。
通常情況下,沒有指定這個屬性,一個應用所有的組件都運行在應用的默認進程中,進程的名字和應用的包名一致。
比如manifest的package="com.example.helloalarm",則默認進程名就是com.example.helloalarm。
<application>元素的process屬性可以為全部的組件設置一個不同的默認進程。
組件可以override這個默認的進程設置,這樣你的應用就可以是多進程的。
如果你的process屬性以一個冒號開頭,進程名會在原來的進程名之后附加冒號之后的字符串作為新的進程名。當組件需要時,會自動創建這個進程。這個進程是應用私有的進程。
如果process屬性以小寫字母開頭,將會直接以屬性中的這個名字作為進程名,這是一個全局進程,這樣的進程可以被多個不同應用中的組件共享。
使用:
AlarmManager的使用步驟
1)獲得ALarmManager實例 ALarmManager am=(ALarmManager)getSystemService(ALARM_SERVICE);
2)定義一個PendingIntent發出廣播
3)調用ALarmManager方法,設置定時或重復提醒
4)取消提醒:
該函數會將所有跟這個PendingIntent相同的Alarm全部取消,怎么判斷兩者是否相同,android使用的是intent.filterEquals(),具體就是判斷兩個PendingIntent的action、data、type、class和category是否完全相同。
例如:
啟動提醒:
// 指定啟動AlarmActivity組件 Intent intent = new Intent(AlarmTest.this, AlarmActivity.class); intent.setAction("111111"); // 創建PendingIntent對象 PendingIntent pi = PendingIntent.getActivity( AlarmTest.this, 0, intent, 0); Calendar c = Calendar.getInstance(); // 根據用戶選擇時間來設置Calendar對象 System.out.println("hourOfDay = " + hourOfDay); System.out.println("minute = " + minute); c.set(Calendar.HOUR, hourOfDay); c.set(Calendar.MINUTE, minute); // 設置AlarmManager將在Calendar對應的時間啟動指定組件 aManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
取消提醒:對應的action必須要一樣
//用於取消的
aManager= (AlarmManager)getSystemService(ALARM_SERVICE);
Intent intent = new Intent(AlarmTest.this, AlarmActivity.class); intent.setAction("111111"); // 創建PendingIntent對象 PendingIntent pendingIntent = PendingIntent.getActivity( AlarmTest.this, 0, intent, 0); aManager.cancel(pendingIntent);
案例一:實現6后秒提醒一次的功能:
下面的代碼寫在Activity或Service里面都行
- AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);//獲取AlarmManager實例
- int anHour = 6 * 1000 ; // 6秒
- long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
- Intent intent2 = new Intent(this, AlarmReceiver.class);
- PendingIntent pi = PendingIntent.getBroadcast(this, 0, intent2, 0);
- manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);//開啟提醒
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);//獲取AlarmManager實例 int anHour = 6 * 1000 ; // 6秒 long triggerAtTime = SystemClock.elapsedRealtime() + anHour; Intent intent2 = new Intent(this, AlarmReceiver.class); PendingIntent pi = PendingIntent.getBroadcast(this, 0, intent2, 0); manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);//開啟提醒
定義廣播接收器:
- public class AlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Toast.makeText(context, "收到定時廣播", 1).show();
- Intent i = new Intent(context, LongRunningService.class);
- context.startService(i);
- }
- }
public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "收到定時廣播", 1).show(); Intent i = new Intent(context, LongRunningService.class); context.startService(i); } }
在配置文件中注冊
注意:這里的process一定要寫,內容貌似可以隨便寫
如果設置定時器的進程被殺死之后,定時器事件就不會觸發。而在Android中,系統在需要時會自動終止后台進程,因此在定時過程中,進程被殺死的可能性是非常之大的,特別是在一些內存較少的設備中,基本上后台進程所設置的定時器很難被觸發。
為了讓定時器在進程被終止后還能觸發,需要對上述實現做一個小的修改:在AndroidMefest.xml中如下定義廣播接收類:
<receiver android:name=".MyReceiver" android:process=":newinst"> </receiver>
<receiver android:name=".AlarmReceiver"android:process=":remote" > </receiver>
案例二:定時提醒功能(提醒一次)。實現原理與上面的一樣,只是計算出了指定的時刻到現在的時間差
補充:Calendar的使用方法:
Calendar c=Calendar.getInstance();
c.set(Calendar.YEAR,2016);
c.set(Calendar.MONTH,04);//也可以填數字,0-11,一月為0
c.set(Calendar.DAY_OF_MONTH, 26);
c.set(Calendar.HOUR_OF_DAY, 21);
c.set(Calendar.MINUTE, 40);
c.set(Calendar.SECOND, 0);
//設定時間為 2011年6月28日19點50分0秒
//c.set(2011, 05,28, 19,50, 0);
Calendar myCal1 = Calendar.getInstance();
long nowTime = myCal1.getTimeInMillis();//這是當前的時間
Calendar myCal2 = Calendar.getInstance();
myCal2.set(Calendar.HOUR_OF_DAY,8);
myCal2.set(Calendar.MINUTE,12);
myCal2.set(2016, 03, 27, 9, 40);//日期要減去一,比如:你要設置4月幾號幾點提醒,這里要填寫03,它默認是從0開始
long shutDownTime = myCal2.getTimeInMillis();
long d = shutDownTime - nowTime;
System.out.println(shutDownTime+"-"+nowTime+"=" +d);
- Calendar myCal1 = Calendar.getInstance();
- long nowTime = myCal1.getTimeInMillis();//這是當前的時間
- Calendar myCal2 = Calendar.getInstance();
- // myCal.set(Calendar.HOUR_OF_DAY,hour);
- // myCal.set(Calendar.MINUTE,minutes);
- myCal2.set(2016, 03, 27, 11, 49);
- long shutDownTime = myCal2.getTimeInMillis();
- Intent intent3=new Intent(this,AlarmReceiver2.class);
- intent3.putExtra("time", shutDownTime-nowTime+"");
- PendingIntent pi3=PendingIntent.getBroadcast(this, 0, intent3,0);
- //設置一個PendingIntent對象,發送廣播
- AlarmManager am=(AlarmManager)getSystemService(ALARM_SERVICE);
- //獲取AlarmManager對象
- long triggerAtTime = SystemClock.elapsedRealtime() + shutDownTime-nowTime;
- am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime , pi3);
Calendar myCal1 = Calendar.getInstance(); long nowTime = myCal1.getTimeInMillis();//這是當前的時間 Calendar myCal2 = Calendar.getInstance(); // myCal.set(Calendar.HOUR_OF_DAY,hour); // myCal.set(Calendar.MINUTE,minutes); myCal2.set(2016, 03, 27, 11, 49); long shutDownTime = myCal2.getTimeInMillis(); Intent intent3=new Intent(this,AlarmReceiver2.class); intent3.putExtra("time", shutDownTime-nowTime+""); PendingIntent pi3=PendingIntent.getBroadcast(this, 0, intent3,0); //設置一個PendingIntent對象,發送廣播 AlarmManager am=(AlarmManager)getSystemService(ALARM_SERVICE); //獲取AlarmManager對象 long triggerAtTime = SystemClock.elapsedRealtime() + shutDownTime-nowTime; am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime , pi3);
案例三:重復提醒功能:5秒后提醒第一次,以后每5秒提醒一次
- //重復提醒
- am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 5*1000+ SystemClock.elapsedRealtime(), 5*1000, pi3);
//重復提醒 am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 5*1000+ SystemClock.elapsedRealtime(), 5*1000, pi3);
終極案例:設置每天8:00提醒
- package activity.MyWeather;
- import java.util.Calendar;
- import java.util.TimeZone;
- import service.Alarm_Service;
- import android.app.Activity;
- import android.app.AlarmManager;
- import android.app.PendingIntent;
- import android.content.Intent;
- import android.os.Bundle;
- import android.os.SystemClock;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.Toast;
- public class AlarmActivity extends Activity implements OnClickListener{
- private Button alarm_bt_YES;
- private Button alarm_bt_quxiao;
- AlarmManager am;
- //PendingIntent pi;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_alarm);
- alarm_bt_YES = (Button) findViewById(R.id.alarm_bt_YES);
- alarm_bt_quxiao = (Button) findViewById(R.id.alarm_bt_quxiao);
- alarm_bt_quxiao.setOnClickListener(this);
- alarm_bt_YES.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- int id = v.getId();
- if(id == R.id.alarm_bt_YES){
- Toast.makeText(this, "已開啟提醒", 0).show();
- Intent intent=new Intent(this,Alarm_Service.class);
- intent.setAction("activity.MyWeathe.alarm");
- PendingIntent pi=PendingIntent.getService(this, 0, intent,0);
- long firstTime = SystemClock.elapsedRealtime(); //獲取系統當前時間
- long systemTime = System.currentTimeMillis();//java.lang.System.currentTimeMillis(),它返回從 UTC 1970 年 1 月 1 日午夜開始經過的毫秒數。
- Calendar calendar = Calendar.getInstance();
- calendar.setTimeInMillis(System.currentTimeMillis());
- calendar.setTimeZone(TimeZone.getTimeZone("GMT+8")); // 這里時區需要設置一下,不然會有8個小時的時間差
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.HOUR_OF_DAY, 8);//設置為8:00點提醒
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.MILLISECOND, 0);
- //選擇的定時時間
- long selectTime = calendar.getTimeInMillis(); //計算出設定的時間
- // 如果當前時間大於設置的時間,那么就從第二天的設定時間開始
- if(systemTime > selectTime) {
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- selectTime = calendar.getTimeInMillis();
- }
- long time = selectTime - systemTime;// 計算現在時間到設定時間的時間差
- long my_Time = firstTime + time;//系統 當前的時間+時間差
- // 進行鬧鈴注冊
- am=(AlarmManager)getSystemService(ALARM_SERVICE);
- am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, my_Time, AlarmManager.INTERVAL_DAY, pi);
- }
- else if(id == R.id.alarm_bt_quxiao){
- Toast.makeText(this, "已關閉提醒", 0).show();
package activity.MyWeather; import java.util.Calendar; import java.util.TimeZone; import service.Alarm_Service; import android.app.Activity; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Intent; import android.os.Bundle; import android.os.SystemClock; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class AlarmActivity extends Activity implements OnClickListener{ private Button alarm_bt_YES; private Button alarm_bt_quxiao; AlarmManager am; //PendingIntent pi; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.activity_alarm); alarm_bt_YES = (Button) findViewById(R.id.alarm_bt_YES); alarm_bt_quxiao = (Button) findViewById(R.id.alarm_bt_quxiao); alarm_bt_quxiao.setOnClickListener(this); alarm_bt_YES.setOnClickListener(this); } @Override public void onClick(View v) { // TODO Auto-generated method stub int id = v.getId(); if(id == R.id.alarm_bt_YES){ Toast.makeText(this, "已開啟提醒", 0).show(); Intent intent=new Intent(this,Alarm_Service.class); intent.setAction("activity.MyWeathe.alarm"); PendingIntent pi=PendingIntent.getService(this, 0, intent,0); long firstTime = SystemClock.elapsedRealtime(); //獲取系統當前時間 long systemTime = System.currentTimeMillis();//java.lang.System.currentTimeMillis(),它返回從 UTC 1970 年 1 月 1 日午夜開始經過的毫秒數。 Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.setTimeZone(TimeZone.getTimeZone("GMT+8")); // 這里時區需要設置一下,不然會有8個小時的時間差 calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.HOUR_OF_DAY, 8);//設置為8:00點提醒 calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); //選擇的定時時間 long selectTime = calendar.getTimeInMillis(); //計算出設定的時間 // 如果當前時間大於設置的時間,那么就從第二天的設定時間開始 if(systemTime > selectTime) { calendar.add(Calendar.DAY_OF_MONTH, 1); selectTime = calendar.getTimeInMillis(); } long time = selectTime - systemTime;// 計算現在時間到設定時間的時間差 long my_Time = firstTime + time;//系統 當前的時間+時間差 // 進行鬧鈴注冊 am=(AlarmManager)getSystemService(ALARM_SERVICE); am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, my_Time, AlarmManager.INTERVAL_DAY, pi); } else if(id == R.id.alarm_bt_quxiao){ Toast.makeText(this, "已關閉提醒", 0).show();
- am = (AlarmManager)getSystemService(ALARM_SERVICE);
- Intent intent=new Intent(this,Alarm_Service.class);
- intent.setAction("activity.MyWeathe.alarm");
- PendingIntent pi=PendingIntent.getService(this, 0, intent,0);
- am.cancel(pi);
- }
- }
- }
am = (AlarmManager)getSystemService(ALARM_SERVICE); Intent intent=new Intent(this,Alarm_Service.class); intent.setAction("activity.MyWeathe.alarm"); PendingIntent pi=PendingIntent.getService(this, 0, intent,0); am.cancel(pi); } } }
注意在Activity中取消alarm時
一定要再重新創建所有的對象包括:Intent,PendingIntent,AlarmManager對象
am = (AlarmManager)getSystemService(ALARM_SERVICE);
Intent intent=new Intent(AlarmActivity.this,Alarm_Service.class);
intent.setAction("activity.MyWeather.alarm");
pi=PendingIntent.getService(AlarmActivity.this, 0, intent,0);
am.cancel(pi);
因為在退出該Activity時或關閉該應用程序時,該Activity會被銷毀里面的所有變量,全局變量都會被銷毀。。。。
而在Service里面就不用這樣每次都創建新的對象了,直接設置為成員變量(全局變量)就行了,因為它不會隨着程序的退出而銷毀
原文鏈接:http://blog.csdn.net/wei_chong_chong/article/details/51258336