2012/05/21

Androidアプリケーション開発で感じたこと

最近は仕事を探していることもあってAndroidでのアプリケーション開発を学ぶ事に時間を費しています。 Eclipseも数年前に比べれば非常に成熟していて、パッケージのアップデートによる相互依存性を解決できずに起動できなくなるような状態には、ほぼならずに安定かつ快適な開発環境を提供してくれています。

Androidアプリケーションの開発に必要なハードルはパソコンと、アプリケーションをGoogle Playで公開するために開発者登録をするクレジットカードくらいで、アプリケーションを開発したいと思えば学生でも気軽に始められるような状況です。

そして開発に必要な情報もまたインターネットで入手する事ができ、StackOverflowなどから情報を引き出す事ができます。

とはいえ、Android 1.6の頃からの情報と3.0以降の情報が錯綜していて、古い情報や制作手法を解説しているページが多い点も気になりました。

情報の入手性と確からしさ

多くの開発者がその経験をまとめた記事をブログにまとめています。 そして、私も含めて、その情報のほとんどは、ある特定のテーマを解説する断片的なものです。

一部にはリファレンスの内容を網羅的に翻訳したり、その上で解説を加えているものもありますが、例外的です。

多くは、(当然のことですが)、特定の操作や目的に対する解決策を1つの記事としてまめています。 例えば、作成した画像ファイルをギャラリーアプリなどに反映させるために、「MediaStoreサービスにデータをinsert()しましょう」といった記事が作成されます。

しかし、同時に管理されている画像ファイルの属性を変更した場合に、アプリでその変更が反映されない事に気がつき、「MediaStoreからレコードを削除して、再度insert()しましょう」といった内容もまた記載されています。

これらは作者の体験として正しいものですし、実際に正しく動きます。 しかし目的を達成するためにはMediaStoreに対して、update()/notifyChange()メソッドを使用しても良いはずです。 システムに対する負荷は、より軽くなるでしょう。

情報としては間違っていないけれど、ベストではない、常に、そういう気がまえで検索エンジンの結果をみる事る事が必要だと、つくづく感じました。

情報の鮮度

GridViewやListViewのパフォーマンスチューニングのために、Googleで検索したいくつかの記事を参考にしましたが、 CursorAdapterの派生クラスを作成したり、O'ReillyのプログラミングAndroidにあるA Network MVCについて触れているものは多くはありませんでした。

本格的なアプリケーションを作成するためには、CursorとCursorAdapterを使い、効率良くViewを描画する事が必要だと感じています。静的な項目をリストすることしかできないために、ArrayListオブジェクトを作ってListViewに渡すようなコードは書きたくありません。

Programming AndroidはSafari Onlineでも読めますし、A Network MVCの項目はお勧めです。 サーチエンジンではフレームワークの解説ばかりで、A Network MVCのCursorとAsyncTaskを使用した非同期更新のパターンを知る事は難しいかもしれません。

開発環境の進化

チューニングのための手法はいろいろ進化していて、Android Platform 3.0以上を対象にEclipseでプロジェクトを作成すれば、ネットワークアクセスしている処理をUIスレッドに記述するだけでANRを避けるためコンパイルに失敗します。

現実にはPlatform 2.3.xを対象にしないHoneycomb以降のみを対象としたアプリを開発する機会は、よほど限定された環境を対象にしない限りは、ないと思います。ネットワークアクセスを行なうアプリケーションを開発していれば、試してみる事をお勧めします。何かAsyncTaskを使っていなかったり、その使い方がまずいところを指摘してくれるかもしれません。

SQLite3とORマッピングオブジェクト

スキーマを記述しない事がスマートだという風潮もありますが、インクリメントアップデートするように構成するなどの工夫をすれば世代管理などの運用はそれほど面倒ではありません。

これを避けるためにORマッピングを使う例もありますが、JavaのReflection APIを使う事でJITの対象から外れ、パフォーマンスダウンを引き起す事は良く知られています。時々使用するメソッドをReflection APIを使って汎用的に実装する事は良いかもしれませんが、ループの中で使う事は避けるべきで、あまり突き詰めずに、ほどほどにするのが正解です。

これまでの経験ではテーブル定義をJavaBeans的にGetter/Setterアクセスするクラスを作成して、適切なコンストラクタとinsert,update(save),restoreぐらいのDBアクセス用のメソッドを個別に実装してあげれば十分だと思っています。 参照のみであれば直接CursorオブジェクトをCursorAdapterでレンダリングに使い、書き換えが不要なレコードを表現するbeanの使用は避けましょう。

DBへのアクセスは局所的に抑え込むため、ContentProviderと、これらのプレースホルダ的なクラス以外から、直接DBアクセスをするような処理はまずい場合が多いと思います。

またSQLite3に関連する処理の説明はいろいろありますが、サンプル的なものが多く、スキーマの更新のような運用性 を考慮していない記述はお勧めできません。

機会があれば、SQLite3の使い方は別にまとめておきたいと思います。

最適化手法の変化

JITが有効になったPlatform 2.2以降と、それ以前では対応に違いがでてきます。 例えばインタフェース型の変数にオブジェクトを格納し、メソッドをコールすると2倍遅い場合があると言われていますが、それはJITを搭載していないプラットフォームでの話しです。JIT搭載環境では6%ぐらいだという記述があります。

最近ではPlatform 2.2以降を対象としても、問題とならないケースもあるでしょう。 反対に1.6以降で対応しなければいけないケースもあり、ハードウェアのスペックもより厳しい事が想定されます。

様々なプラットフォームに合わせて、その最適化手法の優先順位が変わってくる事は知っておくべきです。

適切なスタティックメソッドの実装やクラス内部からのgetter/setterコールを防ぐ事は必要だと思います。 しかしパフォーマンスのために、瑣末なコーディングルールにこだわるよりも、アプリケーションの内部構造とUXの向上にもっと注力しましょう。

この記事で取り上げた品々

0 件のコメント: