LoginSignup
25
26

More than 5 years have passed since last update.

RoboGuice使い方メモ

Posted at

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

参考

25
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
26