我的家園

          我的家園

          Android雜談--鬧鐘詳談

          Posted on 2012-04-15 16:28 zljpp 閱讀(3857) 評論(0)  編輯  收藏

          鬧鐘已經(jīng)學(xué)過一段時(shí)間了,但是對它了解的不是很多,由于最近開發(fā)的一個(gè)小應(yīng)用會(huì)用到這個(gè)功能,所以重新學(xué)習(xí)了一下,以便能在以后忘記的時(shí)候記起來,也方便其他人學(xué)習(xí)

          實(shí)現(xiàn)鬧鐘有很多中方式,比如可以使用Handler+Timer(需依賴應(yīng)用程序生命周期),AlarmManager等,而我們需要時(shí)間服務(wù)不依賴應(yīng)用程序而存在,即應(yīng)用程序啟動(dòng)服務(wù),但是即使關(guān)閉應(yīng)用程序,時(shí)間服務(wù)依然運(yùn)行,這就需要使用AlarmManager了

          ?

          首先需要了解一下鬧鐘需要用得到的知識(shí)點(diǎn)

          1、實(shí)現(xiàn)鬧鐘需要用到AlarmManager來實(shí)現(xiàn),這個(gè)類實(shí)現(xiàn)系統(tǒng)警告服務(wù),可以設(shè)定一個(gè)時(shí)間來完成指定的事情,只要在程序中設(shè)置了警報(bào)服務(wù),就可以通過調(diào)用onReceive()方法執(zhí)行你要做的事情,即使是待機(jī)狀態(tài),也不會(huì)影響運(yùn)行。

          可以通過Context.getSystemService()方法來獲得該服務(wù)。

          獲得AlarmManager對象代碼

          AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);

          ?

          2、常量

          ?

          Constants
          int ELAPSED_REALTIME Alarm time in?SystemClock.elapsedRealtime()?(time since boot, including sleep).
          int ELAPSED_REALTIME_WAKEUP Alarm time in?SystemClock.elapsedRealtime()?(time since boot, including sleep), which will wake up the device when it goes off.
          long INTERVAL_DAY
          long INTERVAL_FIFTEEN_MINUTES Available inexact recurrence intervals recognized by?setInexactRepeating(int, long, long, PendingIntent)
          long INTERVAL_HALF_DAY
          long INTERVAL_HALF_HOUR
          long INTERVAL_HOUR
          int RTC Alarm time in?System.currentTimeMillis()?(wall clock time in UTC).
          int RTC_WAKEUP Alarm time in?System.currentTimeMillis()?(wall clock time in UTC), which will wake up the device when it goes off.

          ?

          AlarmManager.RTC,硬件鬧鐘,不喚醒手機(jī)(也可能是其它設(shè)備)休眠;當(dāng)手機(jī)休眠時(shí)不發(fā)射鬧鐘。

          AlarmManager.RTC_WAKEUP,硬件鬧鐘,當(dāng)鬧鐘發(fā)躰時(shí)喚醒手機(jī)休眠;

          AlarmManager.ELAPSED_REALTIME,真實(shí)時(shí)間流逝鬧鐘,不喚醒手機(jī)休眠;當(dāng)手機(jī)休眠時(shí)不發(fā)射鬧鐘。

          AlarmManager.ELAPSED_REALTIME_WAKEUP,真實(shí)時(shí)間流逝鬧鐘,當(dāng)鬧鐘發(fā)躰時(shí)喚醒手機(jī)休眠;

          ?

          RTC鬧鐘和ELAPSED_REALTIME最大的差別就是前者可以通過修改手機(jī)時(shí)間觸發(fā)鬧鐘事件,后者要通過真實(shí)時(shí)間的流逝,即使在休眠狀態(tài),時(shí)間也會(huì)被計(jì)算。

          ?

          ?

          3、Intent與PendingIntent的區(qū)別及含義

          這里少不了用這兩個(gè)東西,有點(diǎn)經(jīng)驗(yàn)的都明白是個(gè)什么東西,但是他倆的具體區(qū)別確有時(shí)候搞不清楚,所幸放到一起,以便以后方便查詢

          Intent是意圖的意思,它是一個(gè)馬上執(zhí)行的東西,比如Activity跳轉(zhuǎn),執(zhí)行

          Intent intent = new Intent();

          intent.setClass(Test1.this, Test2.class);

          startActivity(intent);

          那么Intent會(huì)馬上從Test1跳轉(zhuǎn)到Test2

          ?

          PendingIntent中Pending意思為即將發(fā)生的,待定。也就是不確定什么時(shí)候會(huì)發(fā)生,可能是未來的某個(gè)時(shí)間發(fā)生,可以理解為一中延遲的Intent,但是它一般會(huì)由別的程序觸發(fā)。

          ?

          Intent intent = new Intent(this, Test2.class);

          PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);

          ?

          PendingIntent可以封裝Activity,BroadcastReceiver,Service,與Intent有區(qū)別的是PendingIntent可以脫離應(yīng)用程序而存在

          ?

          其他參考:

          http://wayfarer.iteye.com/blog/586159

          http://gundumw100.iteye.com/blog/825537

          http://blog.sina.com.cn/s/blog_5da93c8f0100u7pb.html

          http://www.eoeandroid.com/forum.php?mod=viewthread&tid=68063

          ?

          4、公共方法

          ?

          public void?cancel?(PendingIntent?operation)

          取消警報(bào),可以是任何類型(2中的那4中類型),比如取消鬧鐘

          ?

          public void?set?(int type, long triggerAtTime,?PendingIntent?operation)

          設(shè)置警報(bào)

          參數(shù):

          第一個(gè)參數(shù):One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC or RTC_WAKEUP.

          第二個(gè)參數(shù):Time the alarm should go off, using the appropriate clock (depending on the alarm type).

          第三個(gè)參數(shù):Action to perform when the alarm goes off; typically comes from?IntentSender.getBroadcast().

          go off這里是睡眠的意思

          ?

          public void?setInexactRepeating?(int type, long triggerAtTime, long interval,?PendingIntent?operation)

          設(shè)置不精準(zhǔn)重復(fù)周期

          不精準(zhǔn)重復(fù)周期的意思是:比如有很多類似的警報(bào),那么當(dāng)設(shè)置的類型范圍比較大的時(shí)候,這些警報(bào)就會(huì)合并為一個(gè)警報(bào),這樣可以不用每次都執(zhí)行警報(bào),是一種節(jié)能型

          參數(shù)的意思可以參見setRepeating部分

          ?

          public void?setRepeating?(int type, long triggerAtTime, long interval,?PendingIntent?operation)

          設(shè)置精準(zhǔn)重復(fù)周期

          這里有四個(gè)參數(shù)

          第一個(gè)參數(shù):即警報(bào)的類型,一般取值是AlarmManager.RTC和AlarmManager.RTC_WAKEUP,如果為RTC表示為一個(gè)正常的定時(shí)器,如果是RTC_WAKEUP則除了有定時(shí)器外還可以有震動(dòng)或者響鈴。另外就是RTC在手機(jī)睡眠的時(shí)候不發(fā)射警報(bào),而RTC_WAKEUP則在睡眠的時(shí)候也會(huì)發(fā)射警報(bào)

          第二個(gè)參數(shù):第一次運(yùn)行時(shí)要等待的時(shí)間,也就是執(zhí)行延遲時(shí)間,單位是毫秒。這個(gè)參數(shù)有些難理解,有問題歡迎奧手們糾正

          我的理解:

          假如設(shè)置為System.currentTimeMillis()+(10*1000),表示系統(tǒng)會(huì)在設(shè)置警報(bào)10秒后第一次發(fā)送警報(bào),也就是第一次響鈴。這樣警報(bào)服務(wù)會(huì)在10秒后進(jìn)入休眠狀態(tài),但是它依賴于第一個(gè)參數(shù)的類型

          英文原意:Time the alarm should first go off, using the appropriate clock (depending on the alarm type).

          第三個(gè)參數(shù):表示執(zhí)行的時(shí)間間隔,單位是毫秒,也就是每過多久發(fā)射一次警報(bào),一般都是以天為單位

          第四個(gè)參數(shù):一個(gè)PendingIntent對象,即到時(shí)間后要執(zhí)行的操作

          public void?setTime?(long millis)

          設(shè)置系統(tǒng)時(shí)間,需要權(quán)限(android.permission.SET_TIME

          ?

          public void?setTimeZone?(String?timeZone)

          設(shè)置系統(tǒng)默認(rèn)時(shí)區(qū),需要權(quán)限(android.permission.SET_TIME_ZONE

          ?

          一、一個(gè)鬧鐘例子

          這個(gè)例子在網(wǎng)上都可以查得到,但是具體來源已經(jīng)找不到了,所以就直接采用了

          這里需要注意的是TimePickerDialog中的五個(gè)參數(shù)含義,可以參考:

          http://blog.csdn.net/yang_hui1986527/article/details/6839342

          先看圖



          下面的是第一次執(zhí)行時(shí),由于設(shè)置的第一延遲執(zhí)行時(shí)間是System.currentTimeMillis(),所以會(huì)馬上執(zhí)行


          下面的是過了一分鐘后執(zhí)行的效果,因?yàn)樵O(shè)置的周期是1分鐘,不過一般設(shè)置的周期都是以天數(shù)為準(zhǔn)的?

          源代碼

          ?

          package com.loulijun.demo2;
          
          import java.util.Calendar;
          
          import android.app.Activity;
          import android.app.AlarmManager;
          import android.app.PendingIntent;
          import android.app.TimePickerDialog;
          import android.content.Intent;
          import android.os.Bundle;
          import android.view.View;
          import android.widget.Button;
          import android.widget.TextView;
          import android.widget.TimePicker;
          
          public class Demo2Activity extends Activity {
              private TextView tv = null;
              private Button setTime,cancelTime;
              private Calendar c = null;
              @Override
              public void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.main);
                  tv = (TextView)findViewById(R.id.tv);
                  setTime = (Button)findViewById(R.id.setAlarm);
                  cancelTime = (Button)findViewById(R.id.cancelAlarm);
                  //得到日歷實(shí)例,主要是為了下面的獲取時(shí)間
                  c = Calendar.getInstance();
                  setTime.setOnClickListener(new Button.OnClickListener()
                  {
          
          			@Override
          			public void onClick(View arg0) {
          				c.setTimeInMillis(System.currentTimeMillis());
          				int hour = c.get(Calendar.HOUR_OF_DAY);
          				int minute = c.get(Calendar.MINUTE);
          				new TimePickerDialog(Demo2Activity.this, minute, new TimePickerDialog.OnTimeSetListener() {
          					@Override
          					public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
          						//是設(shè)置日歷的時(shí)間,主要是讓日歷的年月日和當(dāng)前同步
          						c.setTimeInMillis(System.currentTimeMillis());
          						//設(shè)置小時(shí)分鐘,秒和毫秒都設(shè)置為0
          						c.set(Calendar.HOUR_OF_DAY, hourOfDay);
          						c.set(Calendar.MINUTE, minute);
          						c.set(Calendar.SECOND, 0);
          						c.set(Calendar.MILLISECOND, 0);
          						
          						Intent intent = new Intent(Demo2Activity.this, AlarmReceiver.class);
          						PendingIntent pi = PendingIntent.getBroadcast(Demo2Activity.this, 0, intent, 0);
          						//得到AlarmManager實(shí)例
          						AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
          						//根據(jù)當(dāng)前時(shí)間預(yù)設(shè)一個(gè)警報(bào)
          						am.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
          						/**
          						 * 第一個(gè)參數(shù)是警報(bào)類型;第二個(gè)參數(shù)是第一次執(zhí)行的延遲時(shí)間,可以延遲,也可以馬上執(zhí)行;第三個(gè)參數(shù)是重復(fù)周期為一天
          						 * 這句話的意思是設(shè)置鬧鈴重復(fù)周期,也就是執(zhí)行警報(bào)的間隔時(shí)間
          						 */
          //						am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+(60*1000), 
          //								(24*60*60*1000), pi);
          						am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+(10*1000), 
          								30000, pi);
          				
          						String msg = "設(shè)置鬧鐘時(shí)間為"+format(hourOfDay)+":"+format(minute);
          						tv.setText(msg);
          					}
          				}, hour, minute, true).show();
          				//上面的TimePickerDialog中的5個(gè)參數(shù)參考:http://blog.csdn.net/yang_hui1986527/article/details/6839342
          			}
                  	
                  });
                  
                  cancelTime.setOnClickListener(new Button.OnClickListener()
                  {
          
          			@Override
          			public void onClick(View v) {
          				Intent intent = new Intent(Demo2Activity.this, AlarmReceiver.class);
          				PendingIntent pi = PendingIntent.getBroadcast(Demo2Activity.this, 0,
          						intent, 0);
          				AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
          				//取消警報(bào)
          				am.cancel(pi);
          				tv.setText("鬧鐘取消");
          			}
                  	
                  });
                  
                  
              }
              private String format(int x)
              {
              	String s = ""+x;
              	if(s.length() == 1)
              		s = "0"+s;
              	return s;
              }
          }
          
          

          ?廣播服務(wù):

          ?

          package com.loulijun.demo2;
          
          import android.content.BroadcastReceiver;
          import android.content.Context;
          import android.content.Intent;
          import android.media.MediaPlayer;
          import android.widget.Toast;
          
          public class AlarmReceiver extends BroadcastReceiver {
          
          	@Override
          	public void onReceive(Context context, Intent arg1) {
          		Toast.makeText(context, "鬧鐘時(shí)間到", Toast.LENGTH_SHORT).show();
          	}
          
          }
          ?

          這里需要在AndroidManifest.xml中注冊廣播

          ?

          <receiver android:name=".AlarmReceiver" android:process=":remote"/>

          ? ---------------------------華麗的分割線--------------------------------------------------------------------

          ?

          下面再看一個(gè)例子,其實(shí)這個(gè)例子是基于上面修改的,增加了用SharedPreferences來保存時(shí)間參數(shù),如果到了時(shí)間則播放音樂,播放音樂是在一個(gè)Service中,用戶可以在取消鬧鈴的時(shí)候取消音樂

          ?

          源代碼

          ?

          package com.loulijun.demo2;
          
          import java.util.Calendar;
          
          import android.app.Activity;
          import android.app.AlarmManager;
          import android.app.PendingIntent;
          import android.app.TimePickerDialog;
          import android.content.Intent;
          import android.content.SharedPreferences;
          import android.os.Bundle;
          import android.view.View;
          import android.widget.Button;
          import android.widget.TextView;
          import android.widget.TimePicker;
          
          public class Demo2Activity extends Activity {
              private TextView tv = null;
              private Button setTime,cancelTime;
              private Calendar c = null;
              private SharedPreferences sp;
              @Override
              public void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.main);
                  tv = (TextView)findViewById(R.id.tv);
                  setTime = (Button)findViewById(R.id.setAlarm);
                  cancelTime = (Button)findViewById(R.id.cancelAlarm);
                  //該配置文件用于存放當(dāng)前設(shè)置的鬧鐘時(shí)間信息
                  sp = getSharedPreferences("alarm_record", Activity.MODE_PRIVATE);
                  //得到日歷實(shí)例,主要是為了下面的獲取時(shí)間
                  c = Calendar.getInstance();
                  setTime.setOnClickListener(new Button.OnClickListener()
                  {
          
          			@Override
          			public void onClick(View arg0) {
          				c.setTimeInMillis(System.currentTimeMillis());
          				int hour = c.get(Calendar.HOUR_OF_DAY);
          				int minute = c.get(Calendar.MINUTE);
          				new TimePickerDialog(Demo2Activity.this, minute, new TimePickerDialog.OnTimeSetListener() {
          					@Override
          					public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
          						//是設(shè)置日歷的時(shí)間,主要是讓日歷的年月日和當(dāng)前同步
          						c.setTimeInMillis(System.currentTimeMillis());
          						//設(shè)置小時(shí)分鐘,秒和毫秒都設(shè)置為0
          						c.set(Calendar.HOUR_OF_DAY, hourOfDay);
          						c.set(Calendar.MINUTE, minute);
          						c.set(Calendar.SECOND, 0);
          						c.set(Calendar.MILLISECOND, 0);
          						
          						Intent intent = new Intent(Demo2Activity.this, AlarmReceiver.class);
          						PendingIntent pi = PendingIntent.getBroadcast(Demo2Activity.this, 0, intent, 0);
          						//得到AlarmManager實(shí)例
          						AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
          						//根據(jù)當(dāng)前時(shí)間預(yù)設(shè)一個(gè)警報(bào)
          						am.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
          						/**
          						 * 第一個(gè)參數(shù)是警報(bào)類型;第二個(gè)參數(shù)是第一次執(zhí)行的延遲時(shí)間,可以延遲,也可以馬上執(zhí)行;第三個(gè)參數(shù)是重復(fù)周期為一天
          						 * 這句話的意思是設(shè)置鬧鈴重復(fù)周期,也就是執(zhí)行警報(bào)的間隔時(shí)間
          						 */
          //						am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+(60*1000), 
          //								(24*60*60*1000), pi);
          						am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+(10*1000), 
          								30000, pi);
          				
          						String msg = hourOfDay+":"+minute;
          						tv.setText("當(dāng)前設(shè)置的鬧鐘時(shí)間:"+msg);
          						sp.edit().putString(msg, msg).commit();
          					}
          				}, hour, minute, true).show();
          				//上面的TimePickerDialog中的5個(gè)參數(shù)參考:http://blog.csdn.net/yang_hui1986527/article/details/6839342
          			}
                  	
                  });
                  
                  cancelTime.setOnClickListener(new Button.OnClickListener()
                  {
          
          			@Override
          			public void onClick(View v) {
          				Intent intent = new Intent(Demo2Activity.this, AlarmReceiver.class);
          				PendingIntent pi = PendingIntent.getBroadcast(Demo2Activity.this, 0,
          						intent, 0);
          				AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
          				//取消警報(bào)
          				am.cancel(pi);
          				tv.setText("鬧鐘取消");
          				//取消鬧鐘的同時(shí)取消音樂
          				stopService(new Intent("com.loulijun.demo2.MUSIC"));
          			}
                  	
                  });
                  
                  
              }
          }

          ?然后是廣播接收器

          ?

          package com.loulijun.demo2;
          
          import java.util.Calendar;
          
          import android.app.Activity;
          import android.content.BroadcastReceiver;
          import android.content.Context;
          import android.content.Intent;
          import android.content.SharedPreferences;
          import android.widget.Toast;
          
          public class AlarmReceiver extends BroadcastReceiver {
          
          	@Override
          	public void onReceive(Context context, Intent arg1) {
          		//獲取配置文件中的信息,如果相同則播放音樂
          		SharedPreferences sp = context.getSharedPreferences("alarm_record", Activity.MODE_PRIVATE);
          		String hour = String.valueOf(Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
          		String minute = String.valueOf(Calendar.getInstance().get(Calendar.MINUTE));
          		String time = sp.getString(hour+":"+minute, null);
          		if(time!=null)
          		{
          			Toast.makeText(context, "鬧鐘時(shí)間到", Toast.LENGTH_SHORT).show();
          			//啟動(dòng)Service播放音樂
          			context.startService(new Intent("com.loulijun.demo2.MUSIC"));
          		}
          	}
          
          }

          ?然后是播放音樂的服務(wù)

          ?

          package com.loulijun.demo2;
          
          
          import android.app.Service;
          import android.content.Intent;
          import android.media.MediaPlayer;
          import android.os.IBinder;
          
          public class MusicService extends Service {
          	private MediaPlayer player;
          	@Override
          	public IBinder onBind(Intent intent) {
          		// TODO Auto-generated method stub
          		return null;
          	}
          	
          	public void onStart(Intent intent, int startId)
          	{
          		super.onStart(intent, startId);
          		player = MediaPlayer.create(this, R.raw.test);
          		player.start();
          	}
          	public void onDestroy()
          	{
          		super.onDestroy();
          		player.stop();
          	}
          
          }

          ?需要在AndroidManifest.xml中增加如下的信息,一個(gè)是注冊廣播,一個(gè)是注冊服務(wù)

          ?

                  <receiver android:name=".AlarmReceiver" android:process=":remote"/>
          
                  <service android:name=".MusicService">
                      <intent-filter>
                          <action android:name="com.loulijun.demo2.MUSIC">
                          </action>
                          <category android:name="android.intent.category.default"/>
                      </intent-filter>
                  </service>
          ?

          ?

          其他參考:

          http://www.cnblogs.com/tara/archive/2011/06/09/2076043.html

          開機(jī)啟動(dòng)Service:http://www.eoeandroid.com/forum.php?mod=viewthread&tid=8195

          http://www.eoeandroid.com/forum.php?mod=viewthread&tid=109957

          AlarmUI到內(nèi)核執(zhí)行流程:http://www.cnblogs.com/Hwangroid/archive/2011/10/13.html

          http://our2848884.blog.163.com/blog/static/14685483420114225055804/








          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 河源市| 平陆县| 利川市| 治多县| 英德市| 福泉市| 龙岩市| 邵东县| 屏山县| 定州市| 玉环县| 南平市| 万载县| 舟山市| 吉林市| 阿瓦提县| 沽源县| 常熟市| 哈巴河县| 靖州| 三穗县| 凤台县| 临猗县| 溆浦县| 公主岭市| 清涧县| 依兰县| 雅安市| 广灵县| 资兴市| 阿勒泰市| 光山县| 穆棱市| 鄂州市| 札达县| 称多县| 北京市| 遵化市| 正镶白旗| 嘉鱼县| 紫金县|