家族ToDo(仮)開発日誌

やりたいことや行きたい場所を家族で共有するためのAndoridアプリの開発日誌(兼Android開発学習メモ)です。

開発日誌(3) : モデルの追加とDI

今回の進捗

作業時間: 1.5h

モデリング

昨今はMVVMとか最近流行(再燃?)らしいが、フレームワークの高度なサポートがないと実装が面倒だという話をどこかで聞いたので、慣れたMVC(偽)な感じで作ろうと思う。

まずはListViewにデータを表示すべく、データモデルを考える。このListViewにはToDoアイテムの一覧を表示したいので、ToDoアイテムのモデルを考える。

f:id:ymkn:20140114235001p:plain

とりあえずこんな感じで良かろう。将来的にはToDoなのかToGoなのかToBuyなのかを区別できるようにしたいし、Ownerが誰かを示すuserなんかもほしいところだが、今日はListViewの動作を確認するのが目的なのでこの程度で良いだろう。

modelパッケージを掘って、クラスを書く。ここでまたAndroid Studioに感動してしまったのだが、コードエディタ上でCommand+Nを押すと小窓が現れてgetter/setterの自動生成なんかが簡単にできたりする。他にもコンストラクタ生成やメソッドのオーバーライドなんかもここからできる。これらが個別のショートカットキーで起動するわけではなく、すべてCommand+Nでいけるのが個人的にはすばらしい。困ったらとりあえずCommand+N押しとけ、みたいな。

AndroidでDI

上記で設計したToDoクラスを得るためのFactoryを作るのだが、とりあえずは適当なToDoを作って返すテスト用のFactoryを作っておき、後々Webから取得する本物のFactoryに差し替えたい。Mockとか使うと良さそうな案件だが正直よくしらんのでDIすることにする。

Dagger

最終的にデータはサーバサイドで管理するので、画面に表示するデータはサーバ側からAPI経由で取得させることになるが、とりあえずAndroidアプリを動かすところから始めたいので、適当にデータを供給するProviderとそれを生成するFactoryを作ろうと思う。まずは適当なデータを返すProviderを使うようにしておき、あとでパラメータか設定ファイルかビルドオプションか何かでサーバ側からデータを取得するProviderに差し替えられるようにしたい。

で、こういうときはやっぱりDIしたくなるわけで、AndroidDIコンテナについて適当に調べた。Guiceが有名のようだが、下記サイトによればDaggerのほうが全ての面において優れているとのことでこれを使うことにした。ライブラリのサイズが小さいようですばらしい。

Dagger

Android Studioのプロジェクトで使えるようにするには、jarを入れるディレクトリ(ここではapp/libsとした)を作ってdagger-1.2.0.jarとdagger-compiler-1.2.0.jarを放り込み、jarを右クリックして[Add as Library]を選べばよい。これでgradleの設定ファイルにエントリが追加され、ビルド時に参照されるようになる。

で、上記だけだとビルド時にライブラリが足らんと怒られるので、javawriter-2.3.1.jarとjavax.inject.jarを入手して追加する必要があった。
(javax.inject.ScopeのNoClassDefFoundErrorがでる)

javax.inject-1-bundle.jar - atinject - Maven bundle for javax.inject - JSR-330: Dependency Injection for Java - Google Project Hosting

ちなみにDagger専用のAnnotationをコードエディタ上で補完できるようにするには、IntelliJ用のプラグインを使うと良いみたい。

square/dagger-intellij-plugin · GitHub

DaggerのAnnotation

あまりDaggerのドキュメントをまじめに読んでいないのだが、とりあえずサンプルコードを見る限り、

  • インスタンス生成ロジックを記述しておくModuleクラス (classに@Module(injects = Inject対象のクラス)を、メソッドに@Providesを付与。メソッド名はprovide***じゃないとダメっぽい?)
@Module(injects = KazokuToDoController.class)
public class KazokuToDoModule {
    private String type = "local";

    public KazokuToDoModule(String type) {
        this.type = type;
    }

    @Provides
    public UserProvider provideUserProvider() {
        UserProvider provider = null;
        if ("net.jibunstyle.kazokutodo".equals(this.type)) {
            // TODO: 本物を返す
        } else {
            provider = new LocalUserProvider();
        }
        return provider;
    }

    @Provides
    public ToDoProvider provideWishListProvider() {
        ToDoProvider provider = null;
        if ("net.jibunstyle.kazokutodo".equals(this.type)) {
            // TODO: 本物を返す
        } else {
            provider = new LocalToDoProvider();
        }
        return provider;
    }
}

で、Injectしたいフィールドに@Injectをつけておく。このときModuleクラスからアクセス可能な可視性である必要がある模様。privateだと怒られたのでControllerとModuleを同じパッケージに入れて、@Injectなフィールドはpackageレベルの可視性にした。

public class KazokuToDoController {
    @Inject UserProvider userProvider;
    @Inject ToDoProvider toDoProvider;
(略)
}

これでいい感じにインスタンスが挿入される。のちのち、Moduleクラスのprovideメソッドの中で外部設定ファイルを読み込み、インスタンス化するクラスを変更できるような仕掛けにしようと思うが、どうも泥臭い。なんかもっとすっきりできる方法があるのではないかと思うが、現時点では本質的でないので後回しにする。

開発日誌(2) : 画面(Activity)の作成とウィジェットの配置

今回の進捗

  • ToDo一覧画面の作成(UIだけ)
  • fragmentに関する調査

作業時間: 2h

Activityを構成するファイル

ウィザードが頑張ってくれたおかげでとりあえず最小限の動くコードセットができた。これを書き換えて自分好みの画面を作っていきたい。

Androidにおいて1つのActivityは2つのXML(activity_*.xmlとfragment_*.xml)と1つのクラス(*Activity.java)で構成されている模様。昔Androidについて調べたときはレイアウトXMLはActivityあたり1つしかなかったような気がするがAndroid 3.0からFragmentという概念が追加されたらしい。

フラグメント - Android 開発入門

要は画面部品の再利用やデバイスに応じた切り替えをしたいがためのテンプレートエンジンのようなものなのだろう。たぶん。

試しにactivity_*.xmlの中を覗いてみるとほぼ空っぽで、FrameLayoutのみが定義されている。ここからfragment_*.xmlを参照しているのかと思いきやXMLに記述はなく、*Activityクラスにて処理としてfragment_*.xmlの内容を紐付けしている模様。うーん。

画面の構成情報がコードに入ってしまっているように見えてなんか嫌だったのでFragmentに関する処理やXML中の記述を削除*1して、fragment_*.xmlを削除し、activity_*.xmlのみにして動くことまでは確認した。イベントハンドラウィジェットインスタンスの扱いが面倒そうなのでFragmentは使わずにいこうと思う。ただAndroid Studioのウィザードに画面を作らせると意地でもFragmentを使おうとしてくるのが鬱陶しいので画面は既存のレイアウトのコピペで作ることにした。

ウィジェットの追加

で、画面にウィジェット(Visual Studioの世界で言うところのコントロール)を追加するわけだが、Android Studioが大変よくできていて、Visual Studioのように直感的に使用できる。ActivityのXMLをエディタペインに開き、Designタブに切り替えてPaletteペインからD&Dすればよい。ウィジェットを選択すればPropertiesペインでプロパティの編集ができる。すばらしい。

まずはデフォルトのRelativeLayoutを削除してLinearLayoutに変更する。で、その上にListViewを追加する。Designタブに切り替えると、適当なテキストが表示されたListViewが追加されたのを確認できる。

ツールバーの三角アイコンを押して実行。Genymotion上でアプリが動くが、データが無いのでまっさらな表示のまま。何とかしてウィジェットにデータをバインドしてやらなければならない。

f:id:ymkn:20140113004036p:plain

*1:PlaceholderFragment内部クラスとonCreateのif文が該当箇所。

try-with-resourceとは

Java SE 7知らないのでちょっと調べたら、処理の終わりで確実にリソースをクローズしたい場合に使う模様。C#のusingみたいなもんか。

よくDB接続処理なんかで↓みたいにして各種リソースを確実に閉じていたけど、

Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
  conn = getConnection();
  stmt = conn.createStatement();
  rs = stmt.executeQuery(“SELECT * FROM hoge”);
 // hogehoge
} catch (Exception ex) {
  ex.printStackTrace();
} finally {
  try { rs.close(); } catch (Exception ex) {}
  try { stmt.close(); } catch (Exception ex) {}
  try { conn.close(); } catch (Exception ex) {}
}

これが↓のように簡単に書けるとのこと。finally句のネストしたtry-catchが省略できて大変いい感じですね。

try (
  Connection conn = this.getConnection();
  Statement stmt = conn.createStatement();
  ResultSet rs = stmt.executeQuery(“SELECT * FROM hoge”); 
) {
  // hogehoge
} catch (SQLException ex) {
  ex.printStackTrace();
}

まあ、今回の開発では使えないんですけどね。

開発日誌(1) : 開発環境の構築と空プロジェクトの実行

今回の進捗

作業時間: 2h

Android Studioのインストール

Javaの開発環境といえばeclipseだが、何から何まで細かく設定しないといけないし、バージョンによってはプラグインが動いたり動かなかったりで個人的には嫌い。普段のPHP Web開発にNetbeansを使っているのでそれでやろうかと思ったが、Google様謹製のIDEがあるとのことでそれを使うことにした。最近にわかに人気急上昇中?のIntellJ IDEAベースらしい。

下記公式サイトからダウンロードしてインストール。特に迷うことはない。

Getting Started with Android Studio | Android Developers

Genymotionのインストール

とりあえず適当なプロジェクトを作ってまず動くところを見ようと思って空のプロジェクトを実行したら、標準のエミュレータがあまりに遅すぎて使い物にならない。

で、最近のAndroid SDKではx86ベースのエミュレータが使えるとのことで下記サイトを参考に試してみたが、うまく動かなかったのであきらめた。

Android SDK の高速エミュレータ、使ってますか? | OPTPiX Labs Blog

さらに調べるとGenymotionというのが良いということでインストールしてみた。これが大当たり。

Genymotion + Android Studio で Android 爆速開発メモ - Qiita [キータ]

要は各AndroidバージョンのVirtualBox仮想マシンイメージを用意してくれていて、それをダウンロードして使えるVirtualBoxフロントエンド的ソフトであった。爆速!というほど速いわけではないが、十分使い物になるレベルになった。

なお、個人使用は無償だが、商用利用は有償のようなので仕事で使う際はライセンス契約が必要である点は頭の片隅に置いておくべし。

スリープ復帰後にはGenymotion仮想マシンを再起動した方がいいかも

あるとき原因不明のエラーが発生して困っていたのだけど、試しに純正エミュレータで動かしたら問題なく、もしやと思いGenymotion仮想マシンを再起動したら問題なくなった。問題を避けるために、スリープ前に仮想マシンを落とすようにし、復帰後に起動させるようにしたらその後は平穏である。

プロジェクトの作成

Android Studioのウィザードで簡単にひな形を作ることができる。

f:id:ymkn:20140113000559p:plain

Minimun required SDK
2.x系はまだ3割ほどユーザが居るようだが我が家は皆Andorid 4.0以上なので無視。今更古いバージョンのAPIを勉強したくないので。
Target SDK
自分のスマホは4.2.xだが、奥さんのスマホが4.0.x系なので。
Compile with
デフォルト。現時点ではAPI19 (Android 4.4)
Language Level
6.0はサポートも切れていることだし7.0へ。ただしMinimun required SDKが19より下の場合、try-with-resourceが使えないとのこと。
Theme
テーマの切り替えとか不要なのでHolo Lightのみ。

f:id:ymkn:20140113000558p:plain

Lanuncher Activity
1枚目の画面なのでチェックした。
Package Name
適切な名前に変更。

ツールバーの三角ボタンを押すとビルドして起動。しばらくすると下記ダイアログが表示され、あらかじめ起動しておいたGenymotion仮想マシンがリストアップされているのでOKして起動。

f:id:ymkn:20140113000600p:plain

というわけで見事起動した。

Android開発の基礎知識を蓄えるために読んだ本

何か新しいことを始めるとき、まず入門用の本を読むようにしている。Webの情報は調べ物には良いが、体系的にまとまっているものは少ないので。Androidの場合は公式ドキュメントが大変充実しているようだが、英語は読むのに時間が掛かるのでパス。

とりあえず基礎を身につけるために下記を買ってさらっと読んでポイントになりそうなところに付箋を貼っておいた。

初歩からわかるAndroid最新プログラミング

初歩からわかるAndroid最新プログラミング

いま調べたら増補改訂版が出ている模様。今買うならこっちですね。

初歩からわかるAndroid最新プログラミング 増補改訂版

初歩からわかるAndroid最新プログラミング 増補改訂版

下記も評判が良かったので買ってみたが、あまり読んでいない。

Google Androidプログラミング入門

Google Androidプログラミング入門

2版も出ていたが、評判が悪かった(内容がだいぶ変わってしまった?)ような記憶がありわざわざ1版を買っている。詳しくは覚えていないので実は2版でもよかったのかもしれないが。

あとは付箋部分を読み返したりWebで検索しつつ頑張る感じ。

開発計画のような何か

だいたい下記のような感じで作ろうと思う。見積もりは根拠なし。調査時間やハマリ時間含めてだいたいカン。この時間くらいで何とかするぞ、という意思表示程度のものにすぎない。Web側は作り慣れているのでそこまで詰まらない見込み。なお、単体テストの時間は含めていない。

  1. Android側(単体) (小計92h)
    1. 環境構築 (8h)
    2. ToDo画面 (小計24h)
      1. ToDo一覧画面 (ダミーデータの表示) (4h)
      2. ToDo詳細画面 (Intentの実験、ダミーデータの表示) (4h)
      3. ローカルストレージに関する調査 (4h)
      4. データの保存・読み込み (ローカルストレージに保存) (4h)
      5. ToDo詳細画面 (データの編集) (8h)
    3. 認証画面 (小計48h)
      1. スタート画面 (ログイン or 認証) (4h)
      2. OAuth技術調査 (ライブラリ選定など) (8h)
      3. GoogleアカウントでのOAuth調査 (8h)
      4. 認証画面 (Googleアカウントでの認証) (16h)
    4. 共有画面 (家族の追加) (小計12h)
      1. 共有画面 (4h)
      2. アカウント名入力によるユーザの追加 (8h)
  2. Web側 (小計56h)
    1. 環境構築 (8h)
    2. DB設計・実装 (16h)
    3. API設計・実装 (32h)
  3. Android側(結合) (小計80h)
    1. データの保存・読み込み (Web API使用) (40h)
    2. 結合テスト (40h)

合計196hくらいで最低限の機能を実現。で、後に下記を作ってひとまずの完成とする。

  • 共有画面にて近くに居る人を家族候補として表示 (小計40h)
    • 実装方法の検討と検証 (24h)
    • 実装・テスト (16h)
  • ToDo詳細情報をサービスから取得 (小計40h)
    • 使えそうなサービスの調査・検証 (24h)
    • 実装・テスト (16h)
  • [共有]機能 (GoogleカレンダーとかTodoソフトとか) (小計40h)
    • 対象アプリの選定、Intent仕様の調査 (24h)
    • 実装・テスト (16h)
  • 認証画面 (Twitter/Facebookでの認証) (小計32h)

合計152h。

というわけで、1日平均60分くらいやれたとして、1年で約360人時、15人日というところでしょうか。1時間だと1年分って意外と少ないな・・。まあ2年も3年もやってるとだらけるので何とかしたいところですね。

家族で行きたい場所、ほしいものを共有する「家族ToDo(仮)」

家族で休日に行きたい場所について、思い立ったときにメモして共有できるツールがほしかったが、下記まとめで紹介されているようなToGo系というかWishList系のサービスはどれもソーシャルな感じで大げさでイマイチ。

未来共有系サービスまとめ - NAVER まとめ

というわけで、ちょうどAndroidアプリ開発の勉強もしたかったこともあり自分で作ることにした。コンセプトは下記の通り。

  • 家族だけで使うToDo&Wishリスト (不特定多数とのソーシャル機能などいらぬ)
  • スマホで簡単に登録したい (Webブラウザからの共有とか)。
  • 簡単登録、登録情報の追加入力不要、 (GoogleアカウントやTwitterFacebookアカウントで認証)
  • 近くで同時にアプリを起動すれば自動で家族候補としてリストアップ、そこから1ボタンで登録可能
  • ToBuy、ToGoは外部サービスのAPI (kakaku.comとかFoursquareとか) で簡単に詳細情報を取得可能
  • とりあえずAndroidのみ。iOSは後回し。Web版は作らない (APIのみの実装)。
  • 他の機能はなるべく委譲。GoogleカレンダーやToDoソフトへの連携。

三日坊主にならないよう、わずかな進捗でも良いので極力毎日作業するようにしたい。

追加アイディア

  • 関連ニュースを表示する機能(もっといきたくなる工夫)