Hello Guice!Androidアプリの作成

今回する事

まずは実際に使ってみます。Hello World!って感じで ありがちな例ですが以下の様なコードを書いていきます。

  • GreetingService
    名前を入力すると挨拶を返すサービスのインターフェース
  • StandardGreetingService
    実際にGreetingServiceの実装。普通の挨拶を返す
  • Client
    GreetingServiceを利用するクラス
  • GreetingModule
    GreetingServiceのインタフェースと実装を結びつけるモジュール

ここでStandardGreetingModuleクラスですが、一般的なDI Containerにおける設定ファイルにあたります。「このインターフェースが要求されたこの実装を使う」といった設定はこの中に書かれる形になります。
Guiceの特徴として設定ファイルであるModuleがコンパイラを経由する事でXMLファイルのようにタイプミスが実行時までわからないといった事を防げます。

実装

前回準備したAndroid Application Projectに変更を加えていきます。Javaファイルを追加する前にLayoutファイルを編集して以下の一行を追加してTextViewにIDを与えるようにします。

ExGuice1/res/layout/activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/main_activity_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />

</RelativeLayout>

追加するクラス群のソースを順に紹介します。ここでは細かい説明は省きます。
まずは、サービスのインターフェースです。

ExGuice1/src/local/exguice1/GreetingService.java
package local.exguice1;

public interface GreetingService {
    String greetingTo(String to_name);
}

サービスを実装したクラスです。

ExGuice1/src/local/exguice1/StandardGreetingService.java
package local.exguice1;

public class StandardGreetingService implements GreetingService {
    public String greetingTo(String to_name) {
        return to_name + "さん、こんにちは!";
    }
}

サービスを利用するクライアント。DIを利用することでサービスの実装には直接依存しないで済んでいます。

ExGuice1/src/local/exguice1/Client.java
package local.exguice1;

import javax.inject.Inject;

public class Client {

    @Inject
    private GreetingService service;
    // .... 1
    public String getMessage() {
        return service.greetingTo("Google Guice");
    }

}

GreetingServiceを利用するためのModuleです。

ExGuice1/src/local/exguice1/GreetingModule.java
package local.exguice1;

import com.google.inject.AbstractModule;

public class GreetingModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(GreetingService.class).to(StandardGreetingService.class);
        // .... 2
    }

}

最初からあったMainActivityクラスです。importの追加が3行、他にコードが4行だけ追加しています。

ExGuice1/src/local/exguice1/MainActivity.java
package local.exguice1;

import com.google.inject.Guice;
import com.google.inject.Injector;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Injector injector = Guice.createInjector(new GreetingModule());
        // .... 3

        Client client = injector.getInstance(Client.class);
        // .... 4

        TextView textView = (TextView) findViewById(R.id.main_activity_textview);
        // .... 5
        textView.setText(client.getMessage());
        // .... 6

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
}

これで完了ですのでアプリを実行してみてください。うまくいくと以下の様なアプリが表示されるはずです。

ここでちょっとだけ説明です。

1番-@Injectアノテーションは、Injectorに対して「このインターフェースの実装したインスタンスをこのフィールドに注入してください」という印です。
Clientのserviceの初期化をしていないのにサービスが実行できたのはこのアノテーションがついていた事でClientインスタンスが作成された後に自動的にInjectorによって注入された事によります。
今回アノテーションをFieldにつけましたので、この注入はField Injectionと呼びます。他のInjectionについては追って説明していきたいと思います。

2番-Moduleのconfigureメソッドの中で使われたbindメソッド。このbindはInjectorに対して「あるインターフェースに対して注入するインスタンスのクラスはこのクラスです」と指定をするためのメソッドです。
このインターフェースと実装を紐づける事をBindingと言いますが、GuiceではこのBindingをいくつかの方法で指定する方法が可能です。今回のbind(INTERFACE).to(IMPLEMENT)の方法をLinked Bindingsと呼びます。
先ほどの@Injectとこのbindの指定によってGreetingServiceのインターフェースに対してはStandardGreetingServiceのインスタンスを注入する動作が決まります。

3番-Injectorの作成。Moduleを使ったInjectorを作成します。
ここでGreetingModuleが指定されていますので、InjectorはModuleでバインドされた注入が可能なInjectorになります。

4番-Clientのインスタンスの作成。指定したクラスのインスタンスをInjectorから得ます。
Injectorは指定されたClientのインスタンスを作成し、作成したインスタンスで@InjectアノテーションをつけられたFieldに対してBindされた実装を注入してから戻します。

5番,6番-Clientのメッセージを取得して結果をTextViewに格納しています。

まとめ

Guiceを使ったDIの流れはこんな感じです。

  1. インターフェースを定義
  2. インターフェースの実装
  3. インターフェースを利用するクライアントを作成し、注入の動作をさせたいところに@Injectアノテーションをつける
  4. Moduleのconfigureメソッドでbindを使ってインターフェースと実装を紐づける
  5. InjectorのインスタンスをModuleを指定して作成する
  6. クライアントのインスタンスをInjectorに作成させる
  7. クライアントのインスタンスを使う

このModuleを変更する事でテストのときにだけ使うMockオブジェクトに入れ替えたりといった事を簡単に行う事が実現できます。
これだけだとFactoryパターンを使って実装してやってもよさそうですが、GuiceではBindingの方法と注入の方法がいくつか用意されていて、API呼び出しにより簡単に利用する事ができますのでFactoryを自前で実装するよりは楽になるかと思います。
基本的な流れはここに書いた通りなのですが、BindingとInjectionについては他の方法もありますので次回以降で説明していこうと思います。

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.