petitviolet blog

    アラームの作り方

    2014-03-06

    QiitaAndroid

    これが正しい方法かどうかわかりませんが、アラームとして機能したのでメモ

    Android でアラームを鳴らすためには、

    1. アラーム時に起動するサービスを PendingIntent を使って指定する
    2. 指定されたタイミングで起動したサービスから sendBroadcast を利用してレシーバーを起こす
    3. 起こされたレシーバーからアラーム時に起動する Activity を Intent によって指定、起動

    というプロセスでいけるようです。 めんどくさいですが。

    サービスとレシーバーを利用するためには権限が必要なため、Manifest に記載する 必要な部分のみ書いています。

    androidmanifest.xml
    
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
    
    <application>
    	<!-- MainActivityなど -->
    	<receiver android:name="AlarmReceiver" >
    		<intent-filter>
    			<action android:name="MyAlarmAction"></action>
    		</intent-filter>
    	</receiver>
    
    	<service android:name="MyAlarmService" ></service>
    
    	<activity android:name="AlarmNotificationActivity" ></activity>
    </application>
    

    action に MyAlarmAction というものをセットしていますが、 これはサービスからレシーバーを起動する際に利用する識別子のようなものです。多分。

    アラームの設定自体を行う MyAlarmManager.java は以下の通り

    myalarmmanager.java
    public class MyAlarmManager {
    	Context c;
    	AlarmManager am;
    	private PendingIntent mAlarmSender;
    
          private static final String TAG = MyAlarmManager.class.getSimpleName();
    
    	public MyAlarmManager(Context c){
    		// 初期化
    		this.c = c;
    		am = (AlarmManager)c.getSystemService(Context.ALARM_SERVICE);
    		Log.v(TAG,"初期化完了");
    	}
    
    	public void addAlarm(int alarmHour, int alarmMinute){
    		// アラームを設定する
    		mAlarmSender = this.getPendingIntent();
    
    		// アラーム時間設定
    		Calendar cal = Calendar.getInstance();
    		cal.setTimeInMillis(System.currentTimeMillis());
    		// 設定した時刻をカレンダーに設定
    		cal.set(Calendar.HOUR_OF_DAY, alarmHour);
    		cal.set(Calendar.MINUTE, alarmMinute);
    		cal.set(Calendar.SECOND, 0);
    		cal.set(Calendar.MILLISECOND, 0);
    
    		// 過去だったら明日にする
    		if(cal.getTimeInMillis() < System.currentTimeMillis()){
    			cal.add(Calendar.DAY_OF_YEAR, 1);
    		}
    		Toast.makeText(c, String.format("%02d時%02d分に起こします", alarmHour, alarmMinute), Toast.LENGTH_LONG).show();
    
    		am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), mAlarmSender);
    		Log.v(TAG, cal.getTimeInMillis()+"ms");
    		Log.v(TAG, "アラームセット完了");
    	}
    
    	public void stopAlarm() {
    		// アラームのキャンセル
    		Log.d(TAG, "stopAlarm()");
    		am.cancel(mAlarmSender);
    		spm.updateToRevival();
    	}
    
    	private PendingIntent getPendingIntent() {
    		// アラーム時に起動するアプリケーションを登録
    		Intent intent = new Intent(c, MyAlarmService.class);
    		PendingIntent pendingIntent = PendingIntent.getService(c, PendingIntent.FLAG_ONE_SHOT, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    		return pendingIntent;
    	}
    }
    
    

    PendingIntent の FLAG についてはhttp://y-anz-m.blogspot.jp/2011/07/androidappwidget-pendingintent-putextra.htmlが詳しいです。 アラームを更新するのか、あるいは前のままにするのか等、用途に合わせて設定して下さい。

    getPendingIntent()で定義しているように、PendingIntent.getServiceで呼び出すサービスを指定します。 MyAlarmManager によって、アラームの時刻を設定したい Activity などからaddAlarm(hour, minute)で簡単にアラームをセットできます。

    次にサービスを見ます。

    myalarmservice.java
    public class MyAlarmService extends Service {
    	private static final String TAG = MyAlarmService.class.getSimpleName();
    
    	@Override
    	public IBinder onBind(Intent intent) {
    		return null;
    	}
    
    	@Override
    	public void onCreate() {
    		Thread thr = new Thread(null, mTask, "MyAlarmServiceThread");
    		thr.start();
    		Log.v(TAG,"スレッド開始");
    	}
    	// アラーム用サービス
    	Runnable mTask = new Runnable() {
    		public void run() {
    			// アラームを受け取るActivityを指定
    			Intent alarmBroadcast = new Intent();
    			// ここでActionをセットする(Manifestに書いたものと同じであれば何でもよい)
    			alarmBroadcast.setAction("MyAlarmAction");
    			// レシーバーへ渡す
    			sendBroadcast(alarmBroadcast);
    			// 役目を終えたサービスを止める
    			MyAlarmService.this.stopSelf();
    			Log.v(TAG,"サービス停止");
    		}
    	};
    }
    

    サービスからの BroadCast な Intent を受け取るレシーバー

    alarmreceiver.java
    public class AlarmReceiver extends BroadcastReceiver {
    	@Override
    	public void onReceive(Context context, Intent intent) {
    		// アラームを受け取って起動するActivityを指定、起動
    		Intent notification = new Intent(context, AlarmNortificationActivity.class);
    		// 画面起動に必要
    		notification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    		context.startActivity(notification);
    	}
    }
    
    

    だんだんコードが減ってきて、言うことが無くなってきてます。

    AlarmReceiver によって起こされる Activity

    alarmnotification.java
    public class AlarmNortificationActivity extends Activity {
    	private MediaPlayer mp;
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.alarm_notification);
    
    		// スクリーンロックを解除する
    		// 権限が必要
    		getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
    				WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
    				WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
    				WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    
    		Toast.makeText(this, "アラーム!", Toast.LENGTH_SHORT).show();
    
    	}
    
    	@Override
    	public void onStart() {
    		super.onStart();
    		Toast.makeText(getApplicationContext(), "アラームスタート!", Toast.LENGTH_LONG).show();
    		// 音を鳴らす
    		if (mp == null)
    			// resのrawディレクトリにtest.mp3を置いてある
    			mp = MediaPlayer.create(this, R.raw.test);
    		mp.start();
    	}
    
    	@Override
    	public void onDestroy() {
    		super.onDestroy();
    		stopAndRelaese();
    	}
    
    	private void stopAndRelaese() {
    		if (mp != null) {
    			mp.stop();
    			mp.release();
    		}
    	}
    
    	@Override
    	protected void onResume() {
    		super.onResume();
    		alarmNowText = (TextView) findViewById(R.id.alarm_now_time);
    		handler.sendEmptyMessage(WHAT);
    		// mam.stopAlarm();
    	}
    }
    

    これでalarm_notification.xmlが表示され、Toast が出るはずです。

    意外とめんどくさいアラームのメモでした。

    アラームを設定する際のベストプラクティスがよくわかっていないので、アドバイス頂けると嬉しいです。

    from: https://qiita.com/petitviolet/items/89a0166f4167753d8844