ActivityやFragmentをまたいだイベントの通知を行うのは、なかなかにやっかいです。
EventBusを使うという手もありますが、ここでは、Androidが標準で持っているインテントのブロードキャストを使ってみることにします。
LocalBroadcastManager
ブロードキャストを使うと言っても、Android OSが持っているブロードキャストと、サポートライブラリのLocalBroadcastManagerが提供しているそれの2種類があるので、ケースに応じて使い分けましょう。
LocalBroadcastManagerはLocalと付いていることからわかるように、自分のアプリの外には影響しませんし、外からの影響も受けません。
ただし、その実装は、(おそらく)EventBusなどと同様に、シングルトンなstatic変数でレシーバーやイベントを管理して配信しているライブラリで、そのインターフェースをOSの提供しているブロードキャストに似せているものに過ぎません。
アンチstatic変数派で、アプリの外の影響は気にしないというのであれば、本来のブロードキャストを使った方がよいかもしれません。
BroadcastReceiverはめんどくさい
BroadcastReceiverを用いたインテントのブロードキャストは、もともと、アプリ間も含めたイベント通知の仕組みなので、とても汎用的にできています。
必要な情報をインテントに詰め込む必要があったり、インテントフィルターを指定したりといった余計な手間がかかるため、しばしばEventBusと比較して面倒と言われてしまいます。
なのであれば、面倒なところは、BroadcastReceiverの派生クラス内に隠蔽してしまうのが、まっとうなオブジェクト指向というものでしょう。
では実装の例示といきましょう
いきなりですが、以下のようなクラスを作ります。
public class MyBroadcastReceiver extends BroadcastReceiver {
private static final String ACTION_INVOKED = "パッケージ名とかを混ぜ込んでユニークな文字列にする.ACTION_INVOKED";
private static final String ID = "ID";
private static final String COUNT = "COUNT";
public interface Callback {
void onEventInvoked(long id, int count);
}
// onReceive()メソッドだと何をしているのかわからないので、
// onReceive()からコールバックを呼ぶようにする(名前大事!)
private Callback callback;
private LocalBroadcastManager manager;
private MyBroadcastReceiver(@NotNull Context context, @NotNull Callback callback) {
super();
this.callback = callback;
manager = LocalBroadcastManager.getInstance(context.getApplicationContext());
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_INVOKED);
manager.registerReceiver(this, filter);
}
public static MyBroadcastReceiver register(@NotNull Context context, @NotNull Callback callback) {
return new MyBroadcastReceiver(context, callback);
}
// コンストラクタでregisterReceiver()しているのだけど、
// そんなこと想像しづらいので、コンストラクタはprivateにして、
// register()というファクトリメソッドを用意する(名前大事!)
@Override
public void onReceive(@NotNull Context context, @NotNull Intent intent) {
String action = intent.getAction();
if (ACTION_INVOKED.equals(action) {
long id = intent.getLongExtra(ID, 0);
int count = intent.getIntExtra(COUNT, 0);
callback.onEventInvoked(id, count);
}
}
// onReceive()内でインテントから必要な情報を引っ張り出して、コールバックを呼ぶ。
public static void sendBroadcast(@NotNull Context context, long itemId, int count) {
Intent intent = new Intent(ACTION_CHEERED);
intent.putExtra(ID, itemId);
intent.putExtra(COUNT, count);
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(context.getApplicationContext());
manager.sendBroadcast(intent);
}
// 必要な情報をインテントに詰め込んで、sendBroadcast()するクラスメソッドを用意しておけば、イベントの配信も簡単。
// onReceive()メソッドの動作と合わせて、完全にインテントの存在を隠蔽してます。
public void unregister() {
manager.unregisterReceiver(this);
}
}
と、こんな感じで実装しておけば、イベントを受ける側としては、
:
MyBroadcastReceiver receiver;
:
receiver = MyBroadcastReceiver.register(context, new MyBroadcastReceiver.Callback {
@Override
void onEventInvoked(long id, int count) {
// なにかやる
}
});
:
// いらなくなったら
receiver.unregister();
:
と、これだけでよくなります。
ちなみに、この手のレシーバーは単一目的ごとに作って、Callbackインターフェースのメソッドは一つにしておくのがオススメです。
AndroidStudioやIntelliJのエディタは親切なので、ごちゃっとしたところをたたんで、
:
MyBroadcastReceiver receiver;
:
receiver = MyBroadcastReceiver.register(context, (long id, int count) → {
// なにかやる
});
:
// いらなくなったら
receiver.unregister();
:
と、表示してくれます。可読性が高いですね。
イベントを送る側はもっと簡単で、
MyBroadcastReceiver.sendBroadcast(1L, 100);
これだけ。EventBusをそのまま使うよりも楽ちんです。
LocalBroadcastManagerを使わない場合は、manager変数がいらないので、コンストラクタ内とunregister()メソッドを、
private MyBroadcastReceiver(@NotNull Context context, @NotNull Callback callback) {
super();
this.callback = callback;
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_INVOKED);
context.registerReceiver(this, filter);
}
public void unregister(Context context) {
if (context != null) {
context.unregisterReceiver(this);
}
}
と、しておくのがいいかと思います。フラグメントから呼ぶ場合に、ContextとしてgetActivity()なんかを使っていると、Activityが破棄されてたときなんかにトラブルのを防げます(その代わり、メモリリークしますが)。
任意のオブジェクトを渡したい
インテントにまとまった情報の塊を渡したい場合は、Parcelableインターフェースを実装しておかなければならないというのも、嫌われる原因になっていると思うのですが、Parcelableはそんなに難しくないです。
ガンガンParcelableで渡しちゃいましょう。
まとめ
・Androidに初めから用意されているから安心
・LocalBroadcastManagerを使うかどうかは、考え方次第
・面倒なところは隠蔽できます
・Parcelableは怖くない
・名前とか可読性とか大事