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

pixivのAndroidアプリの画像取り扱い事情

アプリエンジニアのちょこめろんです。

昨日の弊社で開催させていただいたpotatotips12で発表した「pixivのAndroidアプリの画像の取り扱いについて」に追記した内容を紹介したいと思います。

pixivのAndroidアプリで主に扱っている画像は以下になります。

  最大サイズ(px)   備考
小サイズ 128x128 正方形になるようにクロップ
中サイズ 480x960 長辺が最大サイズより小さい場合は拡大
原寸大 原寸大 オリジナル画像(8MB以下)
うごイラ 600x600 zip(複数の600x600のjpg)

pixivのAndroidアプリの小サイズ=>中サイズ=>原寸大の遷移です。

f:id:chocomelonchan:20141218173008p:plain

こちらがうごイラです。

うごイラはじめました by pixiv事務局 on pixiv

小サイズ

タイリングで画像を表示する際に使用しています。

小サイズの画像の取得にはGoogleのVolleyとSquareのOkHttpを使用しています。 Volley+OkHttpの良いところをざっくりあげると

  • 良しなにキャンセル処理をしてくれる
  • レスポンスキャッシュ
  • カスタムしやすい(スレッド数や遅延時間など)
  • RGB_565(2Byte、サムネイル向き)
  • SPDY対応

pixivのVolleyの設定値は以下になります。

/** ネットワークスレッド数 */
private static final int NETWORK_THREAD_POOL_SIZE = 6; // デフォルト4

/** 最初のレスポンスの到着してから処理を行うまでの遅延時間 */
private int mBatchResponseDelayMs = 50; // デフォルト100

Volleyのお話はTechboosterさんの記事がよくまとめられているのでそちらを参照下さい。

VolleyでOkhttpを使うにはRequestQueueのインスタンス生成時にOkHttpを使用したクラスを渡すだけです。JakeWhartonのgistに実装方法があります。

画像の表示にはVolleyのNetworkImageViewを使用しています。

中サイズ・原寸大

中サイズと原寸大の通信にはOkHttpを使用しています、特に変わったことはしていないです。

ここでは原寸大画像の辛い話をしようかと思います。。。

原寸大画像は8MBまでいけます。

大きな画像を表示するにはLoading Large Bitmaps Efficientlyのようにある程度画像のサイズを縮小してから読み込むというのがセオリーかと思いますが、pixivには少し縮小しづらい事情があります。

pixivには超縦長のイラストをコマ分けして漫画にしたり、複数のイラストをまとめて投稿するようなケースがあります。 例えば、1,000(横)x 30,000(縦)の画像を投稿することができます。 この画像を縮小してしまうと画像が潰れてしまい見えなくなってしまいます。

例えば1,000x30,000の画像をメモリに乗せると

1,000px x 30,000px x 4Byte(ARGB_888) = 120MB

なんと120MBになるのです。これはすごい。。。 悪戦苦闘したお話を書くと長くなりそうなので割愛しますが、最近の端末のメモリが良いので120MBをメモリに乗せてもOOM(Out Of Memory)になることはほとんどなくなりました。(自然解決)

加えて、近々3系以下の端末のサポートを終了することにしたのでほとんどOOMが発生することはなくなるかと思います。3系以下の端末のサポート終了については、シェアの少なさに対してのメンテナンスコストの高さが理由になります。

うごイラ

うごくイラストも原寸大に負けず劣らずメモリを食いつぶします。 画像を複数選択してアップロードする場合の最大枚数は150枚、GIFの場合は150枚を超えることもあります。 例えば150枚の画像の場合を考えると108MBにもなります。

600px x 600px x 150枚 x 2Byte(RGB_565) = 108MB

原寸大の話を考慮するとメモリに乗せても行けそうな感じがしますが、GIFの場合や今後枚数が増えないとも言い切れないことを考慮してメモリに載せない方向で考えました。 zipを解凍し、中のpng画像をすべてディスクキャッシュに保存し、その画像を表示の都度都度読み込んでいくことにしました。

うごイラはframe時間を指定できるのでFPSも気になる所です。ディスクキャッシュから読み込むとFPSに多少不安があるのではと素朴に思うかと思いますがその通りです。せいぜい30FPSほどしかでていません。

しかしながら、60FPSにすると150枚で2.5秒しか再生されないということもあってか60FPSのようなうごイラはほとんどありません。そのためこの実装でもうまくいっているのが現状です。

ここに関してはかなりパフォーマンス改善の余地があるところだと思っているので対応していきたいですね。

最後に

大分改善の余地があるところかと思いますが、現状のpixivのAndroidアプリはこのような形で実装しています。 画像の取得、表示の部分はパフォーマンスチューニングし出すとキリがなさそうなので実装コストの兼ね合いを考えて折り合いをつけるのが大事かと思います。 pixivは画像自体が特殊なので、ほとんどのケースでは既存の素晴らしいライブラリを使用すると良いかと思います。

明日は @tadsan がPHPのいい話をしてくれます。