【実装〜リリース編】アニメアプリのアニマネAndroid版を作った時の話
アニマネAndroid版を作った時の話。
アニマネ 〜 アニメ専門のニュースアプリ - Google Play の Android アプリ
前回はUI構築までの話でした。
今回は実装からリリースまでの話を書いてみます。 細かいコードの話ではなく、実装時に使ったライブラリや悩んだところなどの大雑把な話です。
実装
HTTP通信
Volleyというライブラリが従来からの定番っぽかったのですが、 これからスタンダードになると思われるOkHttpを選択しました。
gzipもそのまま使えるので便利です。
あと、AndroidアプリにおいてHTTP通信を行う時の注意点が2つ。
- パーミッションの許可が必要
- メインスレッドでは実行できない。
パーミッションに関してはネットワークだけでなく、iOSよりも細かく色々と決められています。
参考:AndroidManifest.xml の permission一覧 - [Androidアプリ/Android] ぺんたん info
僕は軽く悩んだので、先にどのようなものがあるかを確認してから作った方がよいかと思います。
あと、HTTP通信はメインスレッドで実行できないという制約があるので、必然的に非同期処理になります。
初期データのローディングとかは同期的にやりたいところだと思いますが、
OSの制約で同期的に処理できません。
この辺は最初に知っていると設計をあとから変更せずに済みます。
今回は初期データを用意したかったので、非同期処理が終わるまでスプラッシュスクリーンを表示するようにしました。
OkHttpでファイルをダウンロードする。
HTTP経由で取得したファイルを保存する方法が分からず悩んでいたのですが、下記のやり方でできました。
上記に記載がありますが、Okioを使うことで簡単にできました。
File downloadedFile = new File(context.GetCacheDir(), filename);
BufferedSink sink = Okio.buffer(Okio.sink(downloadedFile));
sink.writeAll(response.body().source());
sink.close();
SQLite/ORマッパー
個人的にActiveRecordがあまり好みでないので、データ構造自体はPOJOで扱えるライブラリにしました。
少し古いですが、ORMの選定は下記が参考になります。
greenDAOは導入が少し面倒ですが、それだけ乗り越えればあとは楽でした。
アニマネの場合はSQLiteのDBをHTTP経由で取得しているので、オブジェクトとのマッピングはSQLiteのDBになります。 JSONをメインに使うならOkHttpと同じsquere製のretrofitが良さそうでした。
greenDAO
コード生成に別プロジェクトを作っている例が多いですが、 gradleのタスクとして実行できるようにしておくと楽です。
下記が参考になりました。
Tutorial – greenDAO from scratch – Part 1 | DevTeam83 UG (haftungsbeschränkt)
SQLiteでデータを取得する
今回は複数のSQLiteDBを扱ったり、JOINなどの複雑なクエリも多かったので、 データ取得は直接SQLを書く形で実装。
greenDAOはDBオブジェクトを生成できるので、そのオブジェクトをそのまま参照しました。
複数のDB(に加えてAttach Databaseでの連結)を扱う必要があったので、 コネクションマネジャー的なクラスを用意して接続を管理しています。
SQLiteについては下記のエントリを参考になりました。
Y.A.M の 雑記帳: Android あらかじめ作成した SQLite database をアプリに取り込む
databasesディレクトリは自分で作れない
初期データとしてSQLiteのDBをネットワーク経由で取得して保存するだけのはずだったのですが、 ここはかなりハマりました。
- AndroidではSQLiteのDBファイルは/data/data/パッケージ名/databasesでしか保存できないっぽい
- しかし、databasesディレクトリがアプリインストール直後は存在しない。
- 初めて起動した時にSQLiteのDBファイルをネットワーク経由で取得して保存する場合にディレクトリがなくて困る
- コードから作ろうとしてもdatabasesディレクトリは作れない。
- createOrDatabaseメソッドなら作れる
- 苦肉の策としてダミーのDBを起動直後に作成してdatabasesディレクトリを無理やり用意した。
いい解決方法が見つからず、かなりハマりました。
非同期処理
RxJavaというリアクティブプログラミングのライブラリをAndroid向けにしたのがRxAndroidです。
リアクティブ自体はまだ使いこなすに至ってないのですが、 非同期処理に便利との評判だったので、非同期処理から使い始めることにしました。
Promiseパターンっぽく書けるので便利です。
RXAndroidはそのままだと冗長な書き方になるので、ラムダが使えるようにretrolambdaも併せて使えるようにしておくと幸せになれます。
エミュレーター内のファイルを確認する。
SQLiteをデバッグする際にエミュレーター内のファイルを見たかったのですが、 やり方が分からず少しハマりました。
どうやらadbというコマンドを使えばよさそうでしたので、下記を参考にインストール
「adb shell」コマンドで無事ログインできたので、その中でSQLiteのDBファイルの中をチェックしました。
画像の表示
iOSだとSDWebImageが定番ですが、Androidにおいての画像表示はPicassoとGlideが定番のようです。
今回は簡単に使えそうなPicassoを選択しました。
下までスクロールしたタイミングで処理を実行
ニュース記事データは日別に分割しているので、スクロールで最後まで読み込んだら前日分のデータを取得するようにしました。
iOSだとスクロール位置を使いますが、Androidの場合はListViewの要素をどこまで表示しているかでスクロールの位置が判断できます。
(参考) u1aryzの備忘録とか: ListViewのスクロール位置のあれこれ
リストビュー
iOSのUITableViewはAndroidだとListViewになります。
iOSと違ってクラスが色々分かれているので最初はとっつきにくいです。
クラス設計という点ではAndroidのように処理レイヤーに応じて分けるべきですが、小さい処理だとやや面倒です。
(参考)動的なBaseAdapterの使い方 | メモ帳的blog
PullToRefresh
iOSではPullToRefreshを使ってリロードを実装しているので、Androidでも同様にしたかったのですが、 意外に難しくて今回は断念しました。
OSのバージョンによって実装を変える必要があり、その対応に時間が掛かりそうでした。
1. OSで提供されているSwipeRefreshLayoutを使う方法
実装は簡単なのですがAPIレベル19以上が必要なようで、 Ice Cream SandwichやJelly Beanなどの古いOSでは動かず断念。
2. Android-PullToRefreshを使う方法
AndroidにおいてのPullToRefreshの定番(と思われる)Android-PullToRefreshを使う方法です。
SwipeRefreshLayoutは古いOSでは使えませんが、この方法ならできるだろうとトライ。
Android StudioからAndroid-PullToRefreshを使用する
上記記事にもありますが、本家は現在メンテナンスされていません。 色々探していたら、naverがフォークしてメンテしているようでしたので、下記を使うことにしました。
compile 'com.navercorp.pulltorefresh:library:3.2.+@aar' compile 'com.navercorp.pulltorefresh:extra-listfragment:3.2.+@aar'
build.gradleに上記を追加すると使えるようになります。
これで上手くいったと思いきやクラッシュが頻発。
原因を調べるとListFragmentで使う際は、FragmentActivityが前提になっていることが原因でした。
FragmentActivityはFragmentが実装された時に後方互換のために用意されたクラスらしいです。 FragmentとFragmentActivityではライフサイクルが違うようで、これがクラッシュの原因でした。
今回タブを実装するにあたって利用しているDesign Support LibraryのTabLayoutはFragmentが前提になっているので、 FragmentActivityに今さら切り替えるのは時間が掛かりそうだと判断。
あと、このためだけにFragmentActivityは使いたくなかったので、今回はPullToRefreshを断念してFloatingActionButtonでリロードする実装に切り替えました。
また時間ができたらトライしたいと思います。
デバイスの回転
回転は意外と面倒なようで、下記を参考にしました。
ライブラリ選定の参考にしたエントリ
下記が参考になりました。
実機でのデバッグ
検索すれば色々でてきますが、下記の記事を参考にしました。
【Android Studio】実機でデバッグするまでの 3 つのステップ!
Debug しようと思ったら「offline」となってる実機の件
USBデバッグの端末認証が表示されない
初回表示されるはずなのですが、どうも表示されない。
どうやら、Android端末のファイル転送に使っていた「Android File Transfer」と干渉していたっぽいです。 Android File Transferを削除すれば無事認証のダイアログが表示されました。
Google Analyticsの導入
お馴染みのGoogle Analyticsも入れました。
公式のドキュメントではちょっと分かりにくかったので、下記を参考にしました。
Activityなら自動でレポートを送信できるようですが、今回作ったアプリはfragment単位で計測したかったので、 自動送信は行わず、手動で送信するようにしました。
Crashlytics/Fabric
iOSでも使っているクラッシュ情報を集めて閲覧できるCrashlytics。
Androidでも入れました。
公式の説明がとても分かりやすいので、英語でも問題なく導入できますが、不安な方は下記が参考になります。
自動でCrashlyticsの導入に必要なコード変換を行ってくれるのに気付かず、
Crashlyticsが提示された通りにコードの修正を行ってからApplyボタンを押すと大変なことになったのは内緒です。
Proguard
Androidアプリは簡単にリバースエンジニアリングできるようです。
そのリスクを少しでも軽減するための仕組みがProguardです。
主にソースの難読化を行なってくれます。
初回リリースから導入しようとしましたが、少しハマったので今回はあと回しにしました。
実際に確認してないから何とも言えないけど、各種Webサービスで使うAPIキーとかもだだ漏れなのかな?
アニマネは大したソースじゃないので今回は未実装でリリースに踏み切りましたが、 企業アプリとして作る場合、Androidアプリはリバースエンジニアリングしやすいという点に気を付けた方がよいと思います。
APIキーや秘密鍵を入れていると悲惨なことになるかも知れません。
リリース
ストアでの公開は全体的に分かりやすいと思います。
あまり迷うことなく作業ができました。
リリースについては下記が参考になりました。
アイコン
iOS版アニマネのアイコンをそのまま使おうと思いましたが、 せっかくなので新しく作り直しました。
Androidアプリのアイコン設定方法は下記が参考になります。
上記のとおり設定すると、ストア用のアイコンも作ってくれるの楽です
iOS版のアニマネも次回アップデートでは新アイコンにする予定です。
あと、アイコンが反映されない時はプロジェクトを一度クリーンする必要があるみたいです。
スクリーンショットの用意
エミューレーターで撮りました。
公開に必要な種類は下記になります。
全体で最低2枚は必要です。
タブレット向けにリリースする必要がなければ、携帯向けだけ追加すれば大丈夫みたいです。 逆に言えばタブレット向けにもリリースしたければ、タブレットのスクショが必要です。
今回の機能だとたくさんスクリーンショットが必要になるわけではなかったので、各タイプ2枚だけ用意しました。
最初のリリースであまり機能もないので、今回は特に装飾せず素のスクリーンショットです。
もうちょっと機能が増えてきたら整えていきたいなと思います。
APKの作成
Android Studioでは下記が参考になりました。
キーストアに関しては下記を参考にしました。 http://houser-home.net/novel-press/subwindow_keystore.html http://blue-red.ddo.jp/~ao/wiki/wiki.cgi?page=Android+%BA%EE%C0%AE%A4%B7%A4%BF%A5%A2%A5%D7%A5%EA%A4%CB%BD%F0%CC%BE%A4%F2%B9%D4%A4%A6
バージョンの指定
Android Studioではappのbuild.gradleで指定する必要があります。
Webで検索すると、AndroidManifest.xmlで指定している例がありますが、 Android Studioではgradleで指定する必要があるので注意が必要です。
初めてのAndroidアプリを作った雑感
おおまかなアプリの流れはiOSとそんなに変わらない感じです。
まだ軽く作っただけの印象ですが、プログラムとしてはiOSの方が簡単に書けて、 UIの調整はAndroidの方が行いやすい印象です。
Androidの場合、Javaの言語仕様もあるせいか抽象クラスが多くて、それを覚えるのに大変でした。 それに加えてバージョンごとに使えるクラスが異なるなど、プログラムはiOSの方が分かりやすい感じがしました。
逆にUIはAndroidの方が組みやすかったです。 HTMLに近い感じで組めるので、イメージしやすかった。
アニメーションもXMLだけで組めるので、個人的には分かりやすかったです。
複雑なことをしだすとまた変わるかも知れませんがUIは組みやすい印象でした。
あと、Androidは修正してすぐに公開できるのが素晴らしいです。
iOSの場合は審査期間が長いので、ある程度まとまった更新にならざるを得ないのですが、 Androidはリリーススパンを短くできるのでいい感じです。
アニマネとしてはとりあえずAndroid版のリリースまではこぎつけたので、これから少しずつiOS版に近づけていきたいと思います。