[Android Tips] Pub / Sub ライブラリ「Otto」を使って Fragment から Activity にイベントを通知する

2014.05.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Pub / Sub

みなさん、Fragment で発生したイベントを Activity に通知する場合は、どのように実装していますか? Fragment の Listener を作ってみたり、Activity に Public メソッドを作ってみたりといったような方法がありますが、何だかスマートじゃないですよね。

そんなときに役に立つのが Otto というライブラリです。モバイル決済サービスで有名な Square 社が公開しており、Google が公開している Guava ライブラリを Fork して作られています。

このライブラリを使うと、Pub / Sub モデルのメッセージングを非常に簡単に実装できます。Pub / Sub モデルとは簡単に説明すると、2つのクラスをそれぞれ出版者(Publisher)、購読者(Subscriber)として扱い、出版者のクラスが Publish すると購読者が受け取ることができるといったモデルです。デザインパターンでは Observer パターンと呼ばれます。わかりやすい解説はこちらにあるので、理解を深めたいかたはそちらを参照してください。

ということで本稿では、Otto を使って Fragment から Activity に何かしらのイベントを通知するしくみを実装してみたいと思います。

ライブラリの導入

Otto のライブラリは GitHub のリポジトリから Clone できます。また Jar ファイルをこちらからダウンロードすることもできます。Gradle や Maven を使ったインポートもできるようですので、こちらを参照してください。

https://github.com/square/otto

あとは普通にアプリケーションプロジェクトから参照することで利用できるようになります。

使ってみる

それでは実装してみましょう。今回は例題としてFragment に配置した Button をタップしたときに Activity で何かするという処理を実装してみます。

まずはじめに下準備です。Otto で取り扱うクラスはたった1つ、Bus クラスのみです。このクラスはシングルトンで使う必要があるので、まずは Bus クラスを生成する BusHolder クラスを作成します。

BusHolder.java

package com.example.sampleproject;

import com.squareup.otto.Bus;

public class BusHolder {

  private static Bus sBus = new Bus();

  public static Bus get() {
    return sBus;
  }

}

次に Button の Click イベントクラスを作成します。このクラスのインスタンスがメッセージングするオブジェクトになります。何だか ActionScript を思い出すのは私だけでしょうか?w

ButtonClickEvent.java

package com.example.sampleproject;

public class ButtonClickEvent {
  
  public String message;

  public ButtonClickEvent(String message) {
    super();
    this.message = message;
  }

}

次に Fragment の実装です。Button の OnClickListener 内で Bus クラスの post() メソッドで ButtonClickEvent オブジェクトを渡します。

SampleFragment.java

package com.example.sampleproject;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;

public class SampleFragment extends Fragment {
  
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    
    Button button = new Button(getActivity());
    button.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        // イベントを通知する
        BusHolder.get().post(new ButtonClickEvent("ボタンタップしたよ"));
      }
    });
    
    return button;
  }

}

最後に Activity の実装です。まず onResume()onPause() のタイミングでそれぞれ Bus クラスの register() メソッド 、 unregister() メソッドを呼び、Activity を Subscriber として登録(解除)します。そして呼び出したいメソッドに @Subscrive アノテーションを付けます。引数を ButtonClickEvent クラスにすることで、先ほど Publish した通知を受け取ることができます。異なる引数にした場合には一切呼ばれない(Subscriberではないため)ので注意してください。

MainActivity.java

package com.example.sampleproject;

import com.squareup.otto.Subscribe;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {
  
  private final MainActivity self = this;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    getSupportFragmentManager().beginTransaction().add(R.id.container, new SampleFragment()).commit();
  }
  
  @Override
  protected void onResume() {
    super.onResume();
    // Subscriberとして登録する
    BusHolder.get().register(self);
  }
  
  @Override
  protected void onPause() {
    super.onPause();
    // Subscriberの登録を解除する
    BusHolder.get().unregister(self);
  }
  
  @Subscribe
  public void subscribe(ButtonClickEvent event) {
    Toast.makeText(self, event.message, Toast.LENGTH_LONG).show();
  }

}

これで終わりです。実行してみると、Button をタップしたときに呼び出されているのが分かります。Button が全画面に配置しているので Button ぽくないですが、Button ですw

otto

まとめ

最もポピュラーな利用例を紹介しましたが、単純にクラス間のメッセージングをシンプルに行うことができるので、用途はいろいろあると思います。Service や BroadcastReceiver などとの連携でも活躍しそうですね。

参考