Posted at

Android で EventBus を使うときのオレオレプラクティス

More than 5 years have passed since last update.

"EventBus はどこでつかうべきか" の記事を参考に、 Android アプリ開発の際に EventBus を使っています。 EventBus は Acrivity や Fragment 、 Service 、 自分で定義したモデルのクラスなど、好きなところから自由にイベント通知を行える代物ですが、どこでも使える分、使いどころに困りました。自分なりの使い方をメモします。


Activity → モデル → 非同期処理 → モデル → Activity

自分が一番よく使うのはアクティビティからモデルオブジェクトに定義してあるロジックを呼び出して、モデルオブジェクト側では Web API と通信するなどの非同期処理を行い、その結果を Activity に通知するパターンです。

Web API との通信では Volley を使っていて、モデルオブジェクトのメソッドから通信を開始して、その結果はコールバックインターフェースで受け取っています。コールバック処理の結果を EventBus で通知するイメージです。


コードイメージ


アクティビティ

public class MyActivity extends Activity {

private final static String TAG = MyActivity.class.getSimpleName();
private final EventBus eventBus = EventBus.getDefault();

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

final Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyModel myModel = new MyModel();
myModel.someAsyncTask();
}
});
}

public void onEvent(MyModelAsyncTaskEvent event) {
if (event.isSuccess()) {
Log.d(TAG, "My model async task is success");
} else {
Log.d(TAG, "My model async task is failure");
}
}

@Override
protected void onResume() {
super.onResume();
eventBus.register(this);
}

@Override
protected void onPause() {
eventBus.unregister(this);
super.onPause();
}
}



モデル

public class MyModel {

private final EventBus eventBus;

public MyModel() {
eventBus = EventBus.getDefault();
}

public void someAsyncTask() {
VolleyManager.post("http://ip.jsontest.com", new VolleyManagerHandler() {
@Override
public void onResponse(boolean success, JSONObject jsonObject) {
if (success) {
eventBus.post(new MyModelAsyncTaskEvent(true, jsonObject));
} else {
eventBus.post(new MyModelAsyncTaskEvent(false, null));
}
}
});
}
}



イベントオブジェクト

public class MyModelAsyncTaskEvent {

private boolean success;
private JSONObject jsonObject;

public MyModelAsyncTaskEvent(boolean success, JSONObject jsonObject) {
this.success = success;
this.jsonObject = jsonObject;
}

public boolean isSuccess() {
return success;
}

public JSONObject getJsonObject() {
return jsonObject;
}
}


MyModel 中の VolleyManager はそれっぽく書いただけのコードですが、イメージとしてはこんな形です。 EventBus を使わないと、 Activity から someAsyncTask メソッドを呼び出すときの引数に、何からしらのコールバックインターフェースのオブジェクトを渡して、処理が完了したときに呼び出したくなるのですが、 Activity がそのときまで生きているか判らないので怖いですね。

MyModelAsyncTaskEvent はイベント通知用のオブジェクトですが、私はよく、イベントオブジェクトに boolean な success プロパティを持たせて、 Activity 側で処理が成功したか失敗したかを判断できるようにします。


IntentService

EventBus 的に美味しいのは IntentService だと思います。とりあえずキューに詰め込んで、どんどん非同期処理してもらって、終わったところからイベントを通知してもらって、受け取れるならば受け取った結果 UI に反映して …… なんて事がやりやすくなります (した) 。


コードイメージ


IntentService

public class MyIntentService extends IntentService {

public MyIntentService(String name) {
super(name);
}
 
public MyIntentService() {
super(MyIntentService.class.getSimpleName());
}
 
@Override
protected void onHandleIntent(Intent intent) {
(new Handler()).postDelayed(new Runnable() {
@Override
public void run() {
EventBus.getDefault.post(new MyIntentServiceEvent());
}
}, 3000);
}
}


イベントオブジェクト

public class MyIntentServiceEvent() {}


処理の終わりに「終わったよ」を通知する感じですね。受け取り先がいれば、受け取って何かしらやれば良いわけです。イベントオブジェクトに引き渡す物がなければ、イベントオブジェクトはただクラスを定義するだけで OK です。


悩みどころ

EventBus は便利です。 Activity 側のライフサイクルが終了していて、イベントを受け取れなかったとしても EventBus は「イベント受け取り先がなかったよ」と、 LogCat に出力してくれるだけで、エラーが出て落ちるなんて事はありません (もちろん、実際には Activity の onPause などで非同期処理をキャンセルするような処理は必要でしょう) 。

ただ、使っていて悩む場面もあって、それはイベントオブジェクトの粒度です。今回の例では someAsyncTask メソッド1つなので、それに対応するだけのイベントオブジェクトを置きましたが、アプリケーションの規模が大きくなってくると、似たような事をやっているけども、あのクラスには通知したくない、されたくないので別のイベントオブジェクトにする …… なんて場面に出くわします。

こうなると、 Don't Repeat Yourself 的に気持ちが悪いというか、何というかな気分になります。なので、 EventBus を使いたい場合はこまめにイベントオブジェクトのリファクタリングを行って、無駄なくスッキリとした構造にしたいなと思っています。