BroadcastReceiverのonReceiveでUIを更新する

AlarmManager からUIを更新する処理でかなり苦戦したので覚書です。

私がネットで探していてよく見たサンプルはこんな感じです。

[AlarmReceiver.java]

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive( Context context, Intent intent ) {
    	 Toast.makeText(context, "Received ", Toast.LENGTH_LONG).show();
    }
}

ActivityとBroadcastReceiverを別々のファイルにする方法です。
(MainActivityからAlarmReceiverを起動する部分は省略しています)

UIの変更が難しい

これだとToastは出せてもTextViewなどのUIは簡単に更新できませんでした。
おまけに、ActivityとBroadcastReceiverを別のファイルにする必要があり、Androidマニュフェストに自作したレシーバーを登録しないといけません。

プログラムの構成には色々な意見があると思いますが、私としてはBroadcastReceiverだけ別ファイルというのがとても嫌でした。
同じアクティビティに関する処理は、同じソース内でまとめたかったからです。
BroadcastReceiverだけ別ファイルになると、後から見直した時に解りづらいですよね。

というわけで、何とか一つのファイルにまとめる方法を調べてやっと実現できました。

ActivityとBroadcastReeiverを一つのソースファイルにまとめる方法

方法としては、

  1. BroadcastReceiverのインスタンスをクラス内で作成
  2. onResume で作成したインスタンスをレシーバーとして登録
  3. 任意のタイミングでAlarmManagerを起動
  4. 作成したインスタンスでブロードキャストを受け取る

これだけです。
以下がソースファイルです。

[MainActivity.java]

package jp.xxxxx.sampleapp;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;

import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.os.Handler;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {

	private final String ACTION_NAME = "com.xxxxx.action.ALARM";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// 開始ボタン
		Button button = this.findViewById(R.id.button1);
		String str = "Alarm Start";
		button.setText( str );

		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// 時間をセットする
				Calendar calendar = Calendar.getInstance();
				// Calendarを使って現在の時間をミリ秒で取得
				calendar.setTimeInMillis( System.currentTimeMillis() );
				// 5秒後に設定
				calendar.add( Calendar.MILLISECOND, 5000 );

				Intent intent = new Intent();
				intent.setAction( ACTION_NAME );
				PendingIntent pending = PendingIntent.getBroadcast( getApplicationContext(), 0, intent, 0 );

				// アラームをセットする
				AlarmManager am = (AlarmManager) getSystemService( ALARM_SERVICE );
				if ( am != null ){
					am.setExact( AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pending );
					Toast.makeText( getApplicationContext(), "Set Alarm ", Toast.LENGTH_SHORT ).show();
				}

			}
		});

		IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction( ACTION_NAME);
		registerReceiver( mReceiver, intentFilter );

	}


	@Override
	protected void onDestroy() {
		super.onDestroy();

		unregisterReceiver( mReceiver );
	}


	BroadcastReceiver mReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {

			Button button = (Button)findViewById( R.id.button1 );
			button.setText( "from broadcast" );

			Toast.makeText(context, "Ive Received ", Toast.LENGTH_LONG).show();
		}
	};
}

ポイントはBroadcastReceiverのインスタンスをクラス内に作成することです。
つまり、クラス内でnewを使って作成することです。
これにより、BroadcastReceiverのonReceive内でUIの更新ができるようになります。
もう一つはインテントフィルタを設定して、AlarmManagerに渡すインテントにインテントフィルタと同じアクションを与える事です。
この方法だと、Androidマニュフェストにレシーバーを登録しなくてもブロードキャストを受けることができます。

使い終わったらunregistereReceiverも忘れずに。
私はonDestroyでクリアしています。
onStop だとスリープしたら登録を解除されてしまいますからね。

スポンサーリンク