Posted at

RoboGuice使い方メモ

More than 5 years have passed since last update.


RoboGuice とは

Android 用の DI コンテナ。

Google Guice をベースにしている。


インストール


build.gradle

dependencies {

compile 'org.roboguice:roboguice:2.0'
}


Hello World(View をインジェクションする)


実装


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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.guicesample.MainActivity" >

<TextView
android:id="@+id/helloWorld"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />

</RelativeLayout>



MainActivity.java

package com.example.guicesample;

import roboguice.activity.RoboActivity;
import roboguice.inject.InjectView;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends RoboActivity {

@InjectView(R.id.helloWorld)
private TextView textView;

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

this.textView.setText("Hello RoboGuice!!");
}
}



動作結果

android.JPG


説明


  • Activity クラスは、 RoboActivity を継承して作成する。


  • onCreate() をオーバーライドして、 RoboActivityonCreate() を呼ぶ。


  • @InjectView アノテーションで View をインジェクションできる。


レイアウトをアノテーションで設定する


MainActivity.java

package com.example.guicesample;

import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import roboguice.inject.InjectView;
import android.os.Bundle;
import android.widget.TextView;

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {

@InjectView(R.id.helloWorld)
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

this.textView.setText("Hello RoboGuice!!!!");
}
}


@ContentView で Activity をアノテートすることで、レイアウトを設定できる。

onCreate() が大分すっきりした印象。


リソースをインジェクションする


strings.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="app_name">GuiceSample</string>
<string name="hello_message">Hello Guice!!</string>

</resources>



MainActivity.java

package com.example.guicesample;

import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import roboguice.inject.InjectResource;
import android.os.Bundle;

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {

@InjectResource(R.string.hello_message)
private String message;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

System.out.println(this.message);
}
}



LogCat出力

I/System.out(1376): Hello Guice!!


@InjectResource でリソースをインジェクションできる。


システムサービスをインジェクションする


MainActivity.java

package com.example.guicesample;

import javax.inject.Inject;

import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;
import android.view.WindowManager;

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {

@Inject
private WindowManager manager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Display display = this.manager.getDefaultDisplay();
Point size = new Point();
display.getSize(size);

System.out.println("x=" + size.x + ", y=" + size.y);
}
}



LogCat出力

I/System.out(1437): x=800, y=1216


@Injectシステムサービス をインジェクションできる。


POJO をインジェクションする


MyClass

package com.example.guicesample;

public class MyClass {

public String method() {
return "MyClass!!";
}
}



MainActivity.java

package com.example.guicesample;

import javax.inject.Inject;

import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import android.os.Bundle;

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {

@Inject
private MyClass myClass;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

System.out.println(this.myClass.method());
}
}


@Inject で POJO をインジェクションできる。

このへんの使い方は、普通の Google Guice の使い方と同じだと思う。


シングルトンで定義する


SingletonClass.java

package com.example.guicesample;

import javax.inject.Singleton;

@Singleton
public class SingletonClass {

@Override
public String toString() {
return "SingletonClass[hash=" + hashCode() + "]";
}
}



MainActivity.java

package com.example.guicesample;

import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import roboguice.inject.InjectView;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {

@InjectView(R.id.nextPageButton)
private Button nextPageButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

this.nextPageButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, NextActivity.class);
startActivity(intent);
}
});
}
}



NextActivity.java

package com.example.guicesample;

import javax.inject.Inject;

import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import android.os.Bundle;
import android.util.Log;

@ContentView(R.layout.activity_next)
public class NextActivity extends RoboActivity {

@Inject
private SingletonClass singleton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v("roboguice-sample", "onCreate. singleton=" + this.singleton);
}

@Override
protected void onDestroy() {
super.onDestroy();
Log.v("roboguice-sample", "onDestroy");
}
}


@Singleton アノテーションでクラスをアノテートすると、そのクラスのインスタンスはシングルトンになる。

インジェクションは @Inject で行う。

上記実装を動かして、 MainActivityNextActivity を戻るボタンを使いつつ何度か行き来すると、以下のようにログが出力される。


LogCat出力

V/roboguice-sample(1627): onCreate. singleton=SingletonClass[hash=1098067568]

V/roboguice-sample(1627): onDestroy
V/roboguice-sample(1627): onCreate. singleton=SingletonClass[hash=1098067568]
V/roboguice-sample(1627): onDestroy
V/roboguice-sample(1627): onCreate. singleton=SingletonClass[hash=1098067568]

Activity が破棄されたあとでも、常に同じインスタンスがインジェクションされている。

アプリを再起動させれば、別のインスタンスが生成される。


メモリリークの危険性

前述の通り、シングルトンで定義した場合、そのインスタンスはアプリケーションが終了するまでメモリ上から削除されない。

つまり、注意して使わないとメモリリークの原因となりえる。


Context Singleton で定義する


ContextSingletonClass

package com.example.guicesample;

import roboguice.inject.ContextSingleton;

@ContextSingleton
public class ContextSingletonClass {

@Override
public String toString() {
return "ContextSingletonClass[hash=" + hashCode() + "]";
}
}


先ほどの NextPageActivityContextSingletonClass をインジェクションするようにして、アプリを実行する。


LogCatの出力

V/roboguice-sample(1749): onCreate. contextSingleton=ContextSingletonClass[hash=1098269536]

V/roboguice-sample(1749): onDestroy
V/roboguice-sample(1749): onCreate. contextSingleton=ContextSingletonClass[hash=1098326064]
V/roboguice-sample(1749): onDestroy
V/roboguice-sample(1749): onCreate. contextSingleton=ContextSingletonClass[hash=1097928128]

Activity が破棄されるたびに新しいインスタンスが生成されている。

@ContextSingleton でクラスをアノテートすると、そのクラスのインスタンスは Activity のライフサイクル内でシングルトンになる。


インターフェースの実装クラスを指定する


実装


MyInterface.java

package com.example.guicesample;

public interface MyInterface {

String method();
}



Hoge.java

package com.example.guicesample;

public class Hoge implements MyInterface {

@Override
public String method() {
return "hoge";
}
}



MainActivity.java

package com.example.guicesample;

import javax.inject.Inject;

import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import android.os.Bundle;

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {

@Inject
private MyInterface obj;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("obj=" + obj.method());
}
}



MyModule.java

package com.example.guicesample;

import com.google.inject.AbstractModule;

public class MyModule extends AbstractModule {

@Override
protected void configure() {
bind(MyInterface.class).to(Hoge.class);
}
}



res/values/roboguice.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>
<string-array name="roboguice_modules">
<item>com.example.guicesample.MyModule</item>
</string-array>
</resources>


動作確認


LogCat出力

I/System.out(2345): obj=hoge



説明


  • インターフェースに対して、どのクラスのインスタンスをインジェクションするかは、 AbstractModule クラスを継承したクラスの configure() メソッド内で指定する。



    • configure() 内の実装方法については こちら を参考のこと。




  • res/values の下に xml を追加して(roboguice.xml)、そこに roboguice_modules という名前で AbstractModule のサブクラスを定義する。


Activity 以外のクラスを作る

Activity 以外にも Robo* と付く名前のクラスが用意されている。

サービスなどを作るときは、それらのクラスを継承して作成する。

以下が、用意されている Robo* クラスの一部。


  • RoboActivity

  • RoboListActivity

  • RoboExpandableListActivity

  • RoboMapActivity

  • RoboPreferenceActivity

  • RoboAccountAuthenticatorActivity

  • RoboActivityGroup

  • RoboTabActivity

  • RoboFragmentActivity

  • RoboLauncherActivity

  • RoboService

  • RoboIntentService

  • RoboFragment

  • RoboListFragment

  • RoboDialogFragment

  • etc.


テストを動かす

ActivityInstrumentationTestCase2 を使ったテストが動かせるか試す。


MyInterface.java

package com.example.guicesample;

public interface MyInterface {

String method();
}



Hoge.java

package com.example.guicesample;

public class Hoge implements MyInterface {

@Override
public String method() {
return "hoge";
}
}



MainActivity.java

package com.example.guicesample;

import javax.inject.Inject;

import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import android.os.Bundle;

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {

@Inject
private MyInterface obj;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println(this.obj.method());
}
}



MyModule.java

package com.example.guicesample;

import com.google.inject.AbstractModule;

public class MyModule extends AbstractModule {

@Override
protected void configure() {
bind(MyInterface.class).to(Hoge.class);
}
}



MainActivityTest.java

package com.example.guicesample;

import android.test.ActivityInstrumentationTestCase2;

public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {

public MainActivityTest() {
super(MainActivity.class);
}

public void test() {
getActivity();
}
}



LogCat出力

I/System.out(2769): hoge


普通に動かせた。


モジュールを差し替える

テストのときだけインジェクションするクラスを差し替える。


Fuga.java

package com.example.guicesample;

public class Fuga implements MyInterface {

@Override
public String method() {
return "fuga";
}
}



TestModule.java

package com.example.guicesample;

import com.google.inject.AbstractModule;

public class TestModule extends AbstractModule {

@Override
protected void configure() {
bind(MyInterface.class).to(Fuga.class);
}
}



MainActivityTest.java

package com.example.guicesample;

import roboguice.RoboGuice;
import android.app.Application;
import android.test.ActivityInstrumentationTestCase2;

import com.google.inject.Module;
import com.google.inject.util.Modules;

public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {

public MainActivityTest() {
super(MainActivity.class);
}

@Override
protected void setUp() throws Exception {
super.setUp();

Application app = (Application) getInstrumentation().getTargetContext().getApplicationContext();

Module module = Modules.override(RoboGuice.newDefaultRoboModule(app)).with(new TestModule());

RoboGuice.setBaseApplicationInjector(app, RoboGuice.DEFAULT_STAGE, module);
}

public void test() {
getActivity();
}

@Override
protected void tearDown() throws Exception {
super.tearDown();
RoboGuice.util.reset();
}
}



LogCat出力

I/System.out(2851): fuga


RoboGuice.setBaseApplicationInjector() でモジュールを差し替えられる。


ロギングユーティリティ

RoboGuice は、 Ln というロギング用のユーティルクラスを持つ。

標準の Log との大きな違いは、


  1. フォーマットを指定できる。

  2. 出力ログに自動でソースコードの場所やスレッド名などの情報が追加されて出力される(リリースビルドすると出力されなくなる)。


実装例

String message = "Ln Verbose Log";

Ln.v("message=%s", message);


出力

V/COM.EXAMPLE.GUICESAMPLE/MainActivity.java:15(2970): main Ln Verbose Log



Activity のイベントをフックする


MyEventListener.java

package com.example.guicesample;

import roboguice.activity.event.OnCreateEvent;
import roboguice.event.Observes;

public class MyEventListener {

public void handleOnCreate(@Observes OnCreateEvent event) {
System.out.println("MyEventListener");
}
}



MainActivity.java

package com.example.guicesample;

import javax.inject.Inject;

import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import android.os.Bundle;

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboActivity {

@Inject
private MyEventListener listener;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("MainActivity.onCreate()");
}
}



LogCat出力

I/System.out(3205): MyEventListener

I/System.out(3205): MainActivity.onCreate()

特定のイベントオブジェクトを引数に取るメソッドを定義し、そのパラメータを @Observes でアノテートする。

そのメソッドを持つクラスを Acitivity にインジェクションすることで、イベントオブジェクトに対応するイベントをフックできるようになる。

「特定のイベントオブジェクト」には以下のものがある。


  • OnActivityResultEvent

  • OnConfigurationChangedEvent

  • OnContentChangedEvent

  • OnContentViewAvailableEvent

  • OnCreateEvent

  • OnDestroyEvent

  • OnNewIntentEvent

  • OnPauseEvent

  • OnRestartEvent

  • OnResumeEvent

  • OnStartEvent

  • OnStopEvent


参考