ラベル Android 4.4 の投稿を表示しています。 すべての投稿を表示
ラベル Android 4.4 の投稿を表示しています。 すべての投稿を表示

2014年5月26日月曜日

Android で 画面の回転や状態の復元まで考えた Fragment の使い方のガイドライン(自分用メモ)

Fragment を使った画面を作る際に、どのように作ればうまい具合に画面の回転や状態の復元を扱えるかという自分用のメモです。

最初にまとめ

  • 基本方針として可能な限りすべての管理を当該ActivityのFragmentManagerに任せると楽
  • ActivityのフィールドとしてFragmentを保持するのはバッドノウハウな気がする
  • onCreateとonDestroyが呼び出されたからといってインスタンスが生成破棄されているとは限らない、これらはFragmentManagerのタイミング次第
最終的に実装したコードは以下のような感じになりました。

今回の発端

ActionBarのタブに2つのFragmentを格納し、片方はListView, もう片方はGoogle MapsのMapViewを突っ込むようなUIを作っていたのですが、Androidは素人なもので普通に作っていると画面回転とタブの切替時にうまいこと状態を復元するのがなかなか手こずってしまいました。というわけで良いプラクティスを考えてみることにしました。

Activity と Fragmentのライフサイクルを復習

Activityは画面回転時に一度破棄されてしまいます。このような場合はActivityのFragmentManagerが現在管理しているFragment(バックスタックに入っているものが含まれるかどうかは未検証)については、Activity破壊時にFragmentManager経由で自動的にonSaveInstanceが呼び出され、Activity復旧時に自動的にonCreateとonCreateView経由で復元が試みられます。

これとは別に、Activityは破棄されないがFragmentは破棄されるケース、例えばActionBarのタブを切り替えたりNavigationDrawerを選択するなどして同一のActivity上で別の画面Fragmentに遷移する場合もあります。

上記いずれの場合も、可能な限りテキストビューの入力内容やリストのスクロール位置、地図のカメラ位置などを保持することをユーザーから期待されるため、状態の復元が必要になります。画面を回したりタブを切り替えたらスクロール位置が先頭に戻ったらユーザーはイライラするでしょう。

状態の保存はBundleとonSaveInstanceStateを使い、復元はonCreateとonCreateViewを使うのが楽です。

解決策

画面回転などActivity自体の破棄と再生成が自動的に行われるケースであればFragmentManager管理下にあるFragmentについて自動的に再生成が試みられるため大して難しくはないと思います。タブを切り替えたりするケースについては、以下のいずれかが良さそうな気がしています。
  • 解決策1: すべてのFragmentをFragmentManagerにattachされた状態のままにし、タブが切り替えられたら見せないFragmentはhideする
  • 解決策2: 必要に応じてFragmentManagerにattach/detachを行い、そのかわり自分でBundleを作りonSaveInstanceStateを呼び出す
1のメリットはタブ切り替え時に復元がそもそも発生しないため管理が簡単です。確実に動作しますし、再生成も必要ないためパフォーマンスも良いです。デメリットはFragmentおよびFragmentが抱えるView構造をすべて保持し続けるためメモリを大量に消費します。

今回採用した解決策2のメリットはタブ切り替え時にFragmentのView構造をすべて捨てるためメモリが効率的です。MapViewはどうしてもメモリを大量に使うためいくらhide状態とはいえあまり他のタブの後ろにおいておきたくはなかったのでこうしました。デメリットはやはり複雑になります。今回はFragmentのインスタンスフィールドとして一時的にBundleを保持していますが、これは正直なぜFragmentがタブから外れてDetachされてDestroyされてるのにメモリ上に残ってるのかわかりづらい変な挙動になるので、Activity側かまたは何らかのマネージャクラスに任せてしまうべきではないかと思います。・・・ってそれがFragmentManagerなんですけど。もっとうまいやり方で出来そうな気がするんですが・・・


2014年4月30日水曜日

Android で Dagger DI を使いやすくするライブラリを書きました

Daggerというsquare社がオープンソースで提供しているAndroid向けDI (Dependency Injection)フレームワークがあります。


これを試しに自分のAndroidアプリで使ってみようと思い立ったのですが、幾つか問題が発生しました。

  • DI自体の概念が難しい
  • そもそもドキュメントを読んでもDaggerの使い方がよくわからない、公式のサンプルを真似してみても正直いまいちわからない
  • AndroidでDIを行うとなるとandroid.content.Contextの注入が必須になるのだが、Contextは動的なインスタンスであるためDIでの取り扱いが難しい

そこで四苦八苦しながら動くようになったものをライブラリとして公開し、少しでも簡単にDIのメリットだけを享受できればと思いまして DaggeredAndroid なるものを作ってみました。

使い方とかはREADMEを見てください。全部英語ですがすみません(´・_・`)

AndroidでDIを使う際のメリットは主に以下のとおりです。

  • オブジェクトをメンバに接続するだけのコードを無くせるので、コード量が減る。
  • シングルトンの取り扱いが楽になる。
  • Contextの取り扱いが楽になる。
  • Moduleを差し替えればインスタンスが安全に差し替わるので、テスト環境を作ったり、本番と開発環境を分離したりなど、環境の差し替えが楽になる。テスト時のみModuleを上書きすることもできる。

2014年4月20日日曜日

Android の TextView.setText() が遅い場合の原因と対処法

AndroidでTextViewを使っている時に、setText()に数百行単位のテキストを渡すとメインスレッドが1秒弱完全に固まってしまうという現象に見舞われてしまいました。昔の2.3端末ではともかく、手元の最新鋭機Nexus 5 (Android 4.4)でこんなに遅いのでは話になりません。しっかりと原因を調査し対処法を考えることにしました。

まずググってみると出るわ出るわ同じ問題。やはりみんな同じ場所で躓いているようです。
しかしながらいまいち具体的な原因がググっても見つかりません。そこでtraceviewを取ってみました。


すると原因が一発でわかりました。android.graphics.Paint.getTextRunAdvances()です。
Nexus 5では高速化のためJNI経由でネイティブ実装が呼び出されているようですが、それでもまだ間に合わないぐらい遅いようです。それもそのはず、このメソッドは与えられた文字の幅を計算するメソッドです。すなわち数百行のテキストのサイズを計算するため時間がかかっているようです。iOSで例えるならCore TextのCTGryphを計算するようなもの、UILabelのsizeThatFitsを呼び出すようなもので、非常に時間がかかってしまいます。

そこで対処法として、setText()でテキスト全体をセットし直すのではなく、TextViewが裏で保持しているテキストの一部だけを書き換えたり追記したりすることで一度に計算されるテキストのサイズの量を減らして高速化する事を考えました。iOSの場合はUITextViewにはsetText相当のプロパティしか用意されていないので、そのようなことをするのはdelegateを経由してみたりUIKeyInputプロトコルを自前で用意したりなどと困難がつきまとうのですが、Androidの場合は最初からTextViewの裏で保持しているテキストを自在に書きなおすための仕組みが用意されています。

そのためにはまずTextViewの裏で保持されているテキストを「編集モード」にしなければなりません。XMLでandroid:bufferTypeをeditableに指定するか、またはsetText()の第二引数にTextView.BufferType.EDITABLEを指定すると、テキストが編集モードで保持されるようになります。

そうするとgetEditableText()でTextViewが裏側で保持しているテキストが編集可能な状態で取得できます。あとはこのEditableオブジェクトに対して好きなように加工を行うだけです。単にテキストを追加するだけならTextView.append()を実行しても同じ結果が得られます。

こうすると数百行程度であればそれほど遅くなくテキストの追加ができるようになりました。しかしながら1000行を超えてくるとこれでも速度が足りなくなるので、自前でTextViewをサブクラス化して作っていくか、またはListViewにして一度に表示するテキスト量を減らすのが良いと思います。


2014年1月12日日曜日

iOS 開発者が Android 開発者になるために用意したものまとめ


これまでiOS 2.0の登場から今日に至るまでiOS開発者として仕事してきたわけですが、今年はいよいよ本格的に仕事でAndroidの開発を行っていきたいと思っています。そこで正月休みを使って準備したAndroidの開発環境や勉強用の資料をまとめてみました。同じく今年からAndroidやってみよう!という方の助けになればと思います。

■前提条件

まず最初に前提条件として筆者のスペックをまとめてみました。
  • iOS開発歴4年ぐらい (iOS 2~7)
  • Android開発歴半年ぐらい (Android 1.5~2.1の間、仕事では殆ど使ってない)
  • Java歴は3年ぐらい (大学時代と最初の会社でSIerをやっていた間、SJC-P所持だがJava 7とか8とかはわからない)
iOSの開発歴が長く、モバイル開発で必要な要素や落とし穴などをひと通り踏んでいること、少しですがAndroid開発歴があること、Javaについてもひと通りの文法がわかるのを前提としています。従いまして完全にモバイル開発もJavaも初めてという方にはおそらく今回の資料は少しハードルが高過ぎると思います。ご了承ください。

特にJavaについてはどの資料も何の説明もなしに匿名クラスや内部クラスをふんだんに利用するので、最低限Javaの事前勉強をしておいたほうが良いかと思います。

■開発環境の用意

今回はMac上にAndroid Studioをインストールする方針でいきます。
http://developer.android.com/sdk/installing/studio.html
Eclipse+ADTな現場もまだまだ多いかと思いますが、今後は間違いなくAndroid Studioが主流になってくると思われるからです。あとは個人的にEclipseが嫌いというのもあります(´・_・`)

インストール時の注意点としては初回起動時に少々時間がかかるのと、アップデートが必要になる程度です。0.3.2から0.4にアップデートする前にプロジェクトを作ってしまうとアップデート後にビルドが通らなくなってしまったので、最初にまずアップデートすることをオススメします。

Android StudioではEclipse+ADTの環境や、旧来のJava開発とは異なりGradleというビルドツールが何をするにも主体となります。そのためGradleについてある程度の知識が必要となります。幸いにしてWeb上に良い記事がありましたのでこちらを参考に勉強しました。

■シミュレータの用意(Genymotion)

ADTには最初からAndroidエミュレータが付属されていますが、これは残念ながらiOSのシミュレータとは異なり、再現度も悪く異常に動作が遅いため使い物になるシロモノではありません。Android SDK 1.6のころから現在4.4に至るまで全く改善の気配が無いため、おそらく未来永劫改善は望まれないと思われます。その代わりと言ってはなんですがGenymotionというサードパーティ製の非常に使い勝手の良いAndroidシミュレータがありますのでこちらをインストールします。
http://www.genymotion.com

アカウントを作成してFree版をダウンロード・インストールします。Free版と有料版の違いは加速度センサーのエミュレーションや、スクリーンキャスト機能の有無程度なので、Free版でまったく問題ありません。Genymotionの起動にはOracle VirtualBoxが必要になるため、https://www.virtualbox.orgからダウンロードしてインストールしておきます。

最後にAndroid StudioのGenymotionプラグインをPreferences -> Plugins -> Brows RepositoryからGenymotionで検索してインストールして完了です。

■開発機の用意

Genymotionだけでもかなりの開発はできますが、やはりモバイル開発では実機上での確認が必須です。特にAndroidはiOSと異なり端末種類が多い上に端末差異が激しいので、出来る限り多くの実機を用意しておくことが望ましいです。またそれだけではなく、実際に日常生活でAndroidを触り多数のアプリに触れておくことで、Android上でのアプリの作法や最近の流行、iOSとの違いなどを肌で学ぶことができます。

開発用の端末は白ロム(中古品)販売店で安く買うのが良いと思います。通販でも購入可能です。
http://masterka.seesaa.net/article/252845343.html

ですが今回は折角なので最新機種Nexus 5をGoogleから直接購入することにしました。
こちらから直接SIMロックフリーのNexus 5を通販することができます。私の場合は1/1に16GB Whiteを注文して1/5に届きました。正月でなければもうちょっと早かったかもしれません。

届いたら開封して起動すればそのまますぐに使えます。iPhoneと異なりSIMがアクティベーションに必要だとか、iTunesと接続を要求するとかそういう内容は一切ありませんので、SIMを購入しなくても開発機として使用可能なのがいい感じです。ただし最初の言語選択とWiFi接続時のエラーメッセージが非常に不親切なのでちょっとハマりました。

Nexus 5を使ってみての感想ですが、正直信じられないぐらい良いです。Android 2.x時代のダメダメ端末ばかり触っていたのでギャップが大きいというのも有りますが、電池の消耗がiPhoneと比べると激しい以外は欠点らしい欠点が見当たりませんし、余計なものが一切入っていないのでAndroidの長所を存分に味わう事ができます。iOS開発者の人でAndroid開発に手を出したい人にはまずオススメしたいです。

そのままSIMなしで開発機として使っても良かったのですが、3G回線でのテストも行いたかったので、今回はさらにMVMO業者のSIMパッケージを購入しました。初期費用3000円、月額1000円以下、二年縛り無しでSIMが手に入るため非常に安価で便利です。
などがありますが、今回はOCN モバイル Oneを選択しました。

■Hello World!

さて開発環境も開発機も手に入ったので、あとはコードを書くだけです。最初はまず以下のWebサイトを参考資料に簡単なTwitterクライアントアプリを書いてみました。
やはりモバイルアプリ開発の手習いはTwitterクライアントアプリからスタートするのが良いと思います。データのリスト表示、非同期通信、画像の非同期読み込みとUIへの反映、一覧と詳細表示など、モバイルアプリに必要な要素がバランスよく学べます。

■購入した書籍

Webの資料も悪くないですが、やはり最初は書籍を読みながら進めるのが情報がまとまっていて近道だと思います。というわけで以下の書籍を購入しました。すべて達人出版会の電子書籍で手に入るため便利です。
  • Google Androidプログラミング入門改訂2版: http://tatsu-zine.com/books/androidprogramming2ed
    • 入門に必要な内容はひと通り網羅されていると思います。ただし入門書を名乗っていますが、途中からサンプルプログラムが手元にあること前提の説明になったり、コードの一部が説明されていなかったり、Javaがわかること前提のコードが登場したりなど難易度が急激に跳ね上がるので、本当の本当に入門な人には向かないと思います。
  • Effective Android: http://tatsu-zine.com/books/effective-android
    • Effective系は外れないと思って購入しました。Effective Javaも名著でしたしね。
  • Android UI Cookbook for 4.0 ICS(Ice Cream Sandwich)アプリ開発術: http://tatsu-zine.com/books/androiduicookbook40
    • 4.0でUI周りは大きく変わっているので、2.3との互換性維持のノウハウも含めて欲しかったので購入しました。iOSの手習いの時もObjective-Cの本とUIKitの本を選びましたが、最初は言語の本とUIフレームワークの本を選ぶのが良いと思います。
  • Android Security 安全なアプリケーションを作成するために: http://tatsu-zine.com/books/androidsec
    • 仕事でAndroidを使うつもりなのでセキュリティ周りの情報は必須です。セキュリティに関してはiOSよりも更に環境がシビアですので、念には念を入れておきたいです。