読者です 読者をやめる 読者になる 読者になる

リア充爆発日記

You don't even know what ria-ju really is.

RoboGuice2事始め

SNSのようなサーバサイド連携を前提としたAndroidアプリを作ろうと思ったとき、面倒なことの一つにサーバサイド開発との息を合わせるところがあると思う。大半がHttpでデータ取得してそれを画面表示という処理なのに、いちいちHttp発行してらんない。っていうか、初期開発時にはサーバサイドできてないし。

こういうとき、データ仕様だけちゃんとFIXしていれば(どうせあとで変わるけど)Http部分はダミーデータを返すモックに差し替えてそのダミーデータを元にクライアントサイドは開発を進めればいいのだ。

そんなときにベンリなのがDIコンテナだと思うわけですが、Androidだとデファクトと言えるかは微妙だけどどうやらRoboGuiceというのがいいらしい。

これはもともとGoogleで開発・利用されていたGuiceってのをAndroid用にしたものらしく、2012/11時点の最新版は2012/4にリリースされた2.0のよう。

ドキュメント類を眺めてみると実にアップデートされているのか疑問がわく状態で、2012/4に2.0がリリースされてからの動きが一切見えず、採用するかどうか非常に迷った。

結局、公式サイトにあるように、それなりのアプリで採用実績があることと、Twitterアカウントが動き続けているようなので採用に踏み切ることにした。

しかし、チュートリアル的なものが少ないな・・・。

インストール

こちらにガイドがございます。
http://code.google.com/p/roboguice/wiki/Installation

まず、以下のサイトから2つのjarをダウンロードして、適当にlibsとかに置いて、pathを通す。ぼくはNonMavenです。そのうちMaven導入しようと思ってるけど。。。
http://code.google.com/p/roboguice/wiki/InstallationNonMaven

ついでに何かとベンリだろうからsource jarもゲットしてattachしておく。まあ、僕にこのレベルのソースが読み解けるか疑問だけど。

以上。簡単。

動作確認

ここのサンプルを見ながらやってみる。
http://code.google.com/p/roboguice/wiki/InjectView

これだと単にちょびっとだけコードの見通しが良くなる程度で、動作確認以上の意味はそんなにもたないと思うけど、ちょっと自分のに合わせてやってみよう。

もともとこのタイミングでDIを導入してみようと思ったのは

  1. ボタンを押すと、外部のカメラアプリを起動して
  2. 撮影した結果を受け取って
  3. ImageViewに表示する

という件があるとして、3のテストを行うのにわざわざ毎回カメラ起動して撮影して、というのを繰り返すのがめんどくさかったので、これを自動的に固定の画像を返すようにしたくなった、というのと、いずれサーバサイド連携もあるので需要は高いだろう、と思ったからだった。

なので、上記のストーリーに合わせて導入を進めていく。

ちなみに、ここからでてくるソースは公開用にちょこちょこいじってあるので動かないかもしれないです。。

SampleActivity Before

public class SampleActivity extends FragmentActivity {
    private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;

    public static final int MEDIA_TYPE_IMAGE = 1;
    public static final int MEDIA_TYPE_VIDEO = 2;

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

        setContentView(R.layout.sample);

        ImageButton cameraButton = (ImageButton) findViewById(R.id.btn_camera);
        cameraButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

                // TODO data should be saved on sdcard
//                Uri fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
//                intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

                startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);

            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                // this image is not enough for save!!
                Bitmap bitmap = (Bitmap) data.getExtras().get("data");
                preview.setImageBitmap(bitmap);
            } else if (resultCode == RESULT_CANCELED) {
                Log.i(this.getClass().getName(), "camera canceled");
            } else {
                Toast.makeText(this, "failed", Toast.LENGTH_LONG).show();
            }
        }
    }


SampleActivity After

public class SampleActivity extends RoboFragmentActivity {
    private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;

    public static final int MEDIA_TYPE_IMAGE = 1;
    public static final int MEDIA_TYPE_VIDEO = 2;
    @InjectView(R.id.btn_camera) ImageButton cameraButton;
    @InjectView(R.id.btn_save) Button saveButton;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.Theme_Sherlock_Light_DarkActionBar);
        super.onCreate(savedInstanceState);

        setContentView(R.layout.sample);

        cameraButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

                // TODO data should be saved on sdcard
//                Uri fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
//                intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

                startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);

            }
        });

        saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });

    }

地味すぎる・・・。けどまあとにかく動かしてみる。と、落ちた。

java.lang.NoClassDefFoundError: javax.inject.Provider

ぬな。

調べてみると・・・RoboGuice2.0は元になるGuiceは3.0でGuice3.0はJSR-330ってのを使うようになったので・・・ということらしい。

こちらからjarをゲットしてパス通したら動きました。。
http://code.google.com/p/atinject/

今回はここまで・・・。

これ、けっこう大変だから面倒みなきゃいけない人が多いチームとかでの導入は慎重になったほうがいいね。ググれば信頼と実績のstackoverflowを初め、そこそこ情報見つかるけど英語が苦手だと話にならないくらい日本語ドキュメントは期待できないし。。

でも、俺はやるど!本来のゴールに向けて!そこそこ長丁場になる予定だからトータルでは絶対これを実現しといたほうが捗る・・・はず!!


次回、「本来のゴールに向けて」。僕と握手!