モバイルブラウザのキャッシュとデータストレージについて

表題の件について情報を漁った

現時点で裏取り検証をまったくしていないので、議論対象の参考程度でお願いします。

これから実際に手元のプロダクトで検証していく予定ですが、誤読や内容などの疑わしきはTwitterとかでマサカリ投げてください。

ここでは海外のイカしたgeekらが調べてくれた、貴重な情報を信じて話を進めて参ります。素直が一番って、ばーちゃんが言ってました。

Browser Cache

キャッシュと言っても無限の領域ではなく、むしろ現実的に出回っているモバイルデバイスのリソースは、ごくごく有限です。その上でブラウザの振る舞いを理解できていないことを反省して、ちょっと調べてみた次第。

まずはブラウザキャッシュに依存したストラテジを支える、キャッシュコントロール + ユーザー操作に関するブラウザの基本的な振る舞いについて。

リソースのレスポンスヘッダによるキャッシュコントロール指示と、クライアントサイドで各種操作をした際の振る舞いに関するマトリクス図が紹介されています。

アプリのWebView内になると途端に違う振る舞い見せると思うので本当にイヤね('A`)

Persistent / On Memory

Persistentは永続性、On Memoryはオンメモリ管理(揮発性)を指していて、それぞれがブラウザキャッシュを知るためのポイントになる要素です。参考記事から重要そうなところを引用。

以下の内容は出典の偏りと、見解のバラつきがあるため決定的な情報ではないことに注意してください。

The finding that surprised us the most is seeing that iOS does not keep any cache after a browser restart. This is also true for Safari on Windows, but was not the case on a Mac, where Safari showed a respectable 100 MB of persistent cache. Guy's Pod » Blog Archive » Understanding Mobile Cache Sizes The biggest disappointment was Mobile Safari. Unchanged from last year’s tests, Mobile Safari has no persistent cache whatsoever. Whenever browser is restarted or the device power-cycled, all browser cache is cleared. Guy's Pod » Blog Archive » Mobile Browser Cache Sizes – Round 2

iOSのブラウザ(Mobile Safari)を再起動した後に、キャッシュが保持されていないらしいです。WindowsのSafariも同様であるものの、MacのSafariは100MBの永続的キャッシュがちゃんとあるらしい。

注意: Guy's Pod » Blog Archive » Mobile Browser Cache Sizes – Round 2で、5.1.1についても同様に永続的キャッシュは0と示されていますが、iOS 6の検証については不明。(リソース探したい)

Android’s smartphone cache is barely enough to hold 15 pages together (average page weights 400KB); iOS’s memory cache is highly unpredictable, and disappears when the browser restarts; Guy's Pod » Blog Archive » Understanding Mobile Cache Sizes Memory cache proved – then and today – to be extremely unpredictable. Repeated tests showed dramatically different results, with one test often implying a cache size 2-3 times bigger than the other’s. Regardless of whether memory cache is indeed this volatile, or the methodology (explained below) doesn’t measure it well, there’s no point posting these numbers. Guy's Pod » Blog Archive » Mobile Browser Cache Sizes – Round 2

Androidのキャッシュはページ平均400KBで15ページ程度しっかりキャッシュできる反面、iOSはメモリキャッシュであるため、キャッシュの保持サイクルを殆ど予測しようがなく、ブラウザ再起動ですべて揮発してしまう、と結んでいます。また、後続した記事における追検証でも、結果のぶれが大きい過ぎることから、どの程度のキャッシュを保持できるかという数値の提示を諦めています。

ここまでの永続化とメモリキャッシュに関する調査については、1つのブログで調べられた内容を引用しています。しかし、冒頭にあげているブラウザの振る舞いに関するマトリクス図を提示していた記事では次のように述べられています。

Also, while some research out there suggests that the iOS cache isn’t persistent (it reportedly gets wiped after the device shuts down), we didn’t find this in our research. Early findings: Mobile browser cache persistence and behaviour

「そういう挙動がある、って言われてるけど、うちらが今回調査してみたらそういうの見つからなかったわー」と。ここではiPhone 3GS (iOS version 5.1.1)でテストされています。元記事では少ない画像しか試していないから、複数種類のリソースをまとまった量でテストしたらまた結果が違うのかも、というようにも述べていて、何もかも信じられなくなってくる展開ですね。

古い端末の場合はスペック的にメモリの余裕ない → 永続的に使えるディスクキャッシュを使う などの選択的な挙動がある、とかいう新展開も可能性としては考えられますね。あくまで想像ですが。

ということで、決定的な情報までは至ってないように見えますが、そもそも最新iOS 6での検証情報も見つけられていないので、最新の調査情報が共有されることを待ち望む次第。

WebStorage

そんなに罠のある仕様ではありません。同期的なディスクアクセスで遅いという話もありましたが、刺し違いにしている対象がネットワークコストであれば、多くのケースで価値があるんじゃないでしょうか。

A mostly arbitrary limit of five megabytes per origin is suggested. Web Storage - 5 Disk space

WebStorageの仕様では、1オリジンにつき、5MB程度提供することがおすすめされています。しかし1点、注意が必要。

Strings in JavaScript are UTF-16, so each character requires two bytes of memory. This means that while many browsers have a 5 MB limit, you can only store 2.5 M characters. Web Storage Support Test

大雑把には、JavaScriptで使われる文字列は(内部的には)UTF-16で1文字で2バイト喰うから、ブラウザの仕様として5MBだとしても実際には2.5M分の文字しかストアできない、とのこと。なるほど。マルチバイト文字列としてのCKJ(Chinese, Korean, Japanese)を突っ込んだらさらに4バイトになるのかな。

そして、もしもWebStorageの容量がシビアな問題になるようであれば、テキスト情報をより多く保存できるように最適化する手法がFT Labsで紹介されています。

compress, decompressをかける分のオーバーヘッドはありますが、そこまでくると各々のアプリケーションアーキテクチャ事情による話なので、各自が判断してそのコストを受け入れれば良いでしょう。

ただし、iOS5.1のWebViewにおけるlocalStorage

1点注意が必要なポイントとして、Senchaのブログで次のような記事があります。

iOS 5.1 doesn’t offer many new features, and it does take a step backwards. For hybrid apps (web apps packaged in a native shell), iOS 5.1 breaks localStorage and WebSQL persistence, so developers can’t rely on them anymore. HTML5 Scorecard: The New iPad and iOS 5.1 — A Mixed Bag | Blog | Sencha

iOS 5.1は機能が増えた反面として、WebViewでlocalStorageとWebSQLの永続性がぶっ壊れているので、ハイブリッドアプリでそれらを頼れなくなった点が後退したと述べています。

A new WebKitStoreWebDataForBackup info.plist Boolean feature where we can define that we want localStorage and Web SQL databases to be stored in a place to be backed up, such as in iCloud. This problem has appeared in iOS 5.01, now it’s solved iPhone 5 and iOS 6 for HTML5 developers, a big step forward: web inspector, new APIs and more | Breaking the Mobile Web

iOS 6ではWebKitStoreWebDataForBackupというフラグがinfo.plistに追加されて、iCloudにlocalStorageやWebSQLを保存して永続化できるそうです。iOS6は大丈夫で良かったね、という感はありつつiOS 5.1もビジネス的には、まだ絶滅扱いできないので困りものです。

iOS 6つながりの別件で、ホーム画面に登録されたアプリ(apple-mobile-web-app-capable指定)のデータストレージがサンドボックス化された点の周辺情報についてはImportant: iO6 and Data Storage | NSB Blogにまとまっています。ホーム画面に登録するアプリを作っている場合は、参考になるかもしれません。

Application Cache

前項とは対照的に、副作用の強い仕様であるイメージが強いです。過去の自分含めて、安易にぜんぶキャッシュさせちまえ!という愚行に及ぶと、途端に悪辣な実装に成り下がりますが、仕様を理解して、適切に管理対象を選べば強力です。

人様がまとめてくださった情報でいうと、これらがとても参考になると思います。自分が過去に書いた.manifest時代の記事なんかは、とっくに賞味期限切れなのでダメです。

  • .appcacheの配信時、MIMEタイプtext/cache-manifestを指定すること
  • .appcacheの管理対象は、一箇所でも変更があれば常にフルリフレッシュであること
  • よって、更新周期を踏まえてどこがキャッシュされたら効果的か見定めること
  • 検証環境でApplication Cacheのクリア方法を確認しておくこと

上の記事と重複するところもありますが、このあたりのポイントを抑えておけば、大分有効に使えるようになるんではないでしょうか。

教訓的に、効果は強いけど、仕様が大ぶり(間違えると副作用が出やすい)という点を自覚して、うまく使っていきたいところです。

Conclusion

現状、モバイルブラウザのキャッシュ/ストレージ機構を素直に信用することは、パフォーマンス面での突き詰めが必要なシーンにおいては脇が甘いのかもしれません。

その上で、HTML5の周辺実装として用意されている強力なAPIを利用することで、より効果的なキャッシュ機構を備えることができます。たとえば、x3-js - JavaScript rapidly reloaderとか。試してみたい。

特にキャッシュ周りは、開発中の検証だと実感しづらそうな振る舞いも含んでいます。キャッシュコントロール系のレスポンスヘッダの付与だけで満足せず、ブラウザの振る舞いについて注意深く学ぶ必要がありそうです。

そんな感じで。