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

野次馬エンジニア道

野次馬な気持ちでプログラミングをあれこれと綴ります

Android 2.3 の WebView のクロスドメインリクエスト(続編)

ここ数ヶ月かなり忙しく更新をサボっていたので疑問に思っていたことを。

モバイルのサイトをやっているとどうしても避けられないのが古いデバイスへの対応。 悩ましいのがAndroid2.3のようなデバイス達*1。例え中古で二束三文で売られていたしてもは出来の悪いかわいい子などとは到底思えない。

CSSや操作に関する問題は別のサイト*2に譲るとして、血の気が引きそうな

uupaa.hatenablog.com

の記事に関して自分でもう一度調べて(勝手に)続編を書いてみた。

結果: 再現 (但し条件あり)

「最初の1回だけ」というのがポイントのよう。つまりキャッシュに起因した問題。 前述のブログの検証の通りjQueryでテストコードを書いて検証した。 ここでいうキャッシュとは、$.ajax({ cache: true のこと。

2回目のクロスドメインリクエスト処理結果

NGの場合はstatus=0 が返ってくる。

OS Version Browser Cache JSONP GET POST DELETE
Android2.3.4 標準ブラウザ 有効 OK NG OK NG
無効 OK OK OK NG
WebView 有効 OK NG OK NG
無効 OK OK OK NG
Android2.3.5 標準ブラウザ 有効 OK NG OK NG
無効 OK OK OK NG
WebView 有効 OK NG OK NG
無効 OK OK OK NG

Android4.0.4 と Android4.4.2では全てOK。iOSでも問題なし。特にWebViewと標準ブラウザでの挙動の違いは見られない。 JSONPとPOSTを使っていれば問題無しという結果に。

DELETEがキャッシュを無効にしても失敗?

次の疑問はDELETEの列。ヒントになったのはキャッシュ無しをどう実現しているかだった。

XMLHttpRequestのキャッシュ

W3Cの記述を取り上げている こちらの記事を見ても

var req = new XMLHttpRequest();
req.open ('GET', 'test.txt', true);
req.setRequestHeader('Pragma', 'no-cache');  // HTTP/1.0 における汎用のヘッダフィールド
req.setRequestHeader('Cache-Control', 'no-cache');  // HTTP/1.1 におけるキャッシュ制御のヘッダフィールド
req.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT'); // 指定日時以降に更新があれば内容を返し、更新がなければ304ステータスを返すヘッダフィールド。古い日時を指定すれば、必ず内容を返す

のように設定すればよいと思われる。実際クロスドメインでこれらのヘッダを設定するにはおそらく Access-Control-Allow-Headers などサーバ側の対応も必要(今回は試せていない)。

jQueryajaxのキャッシュ

jQueryのキャッシュはこのXMLHttpRequestのものとは別でソース(http://code.jquery.com/jquery.js)を見てみると

// Add anti-cache in url if needed
if ( s.cache === false ) {
        // 省略 
    // Otherwise add one to the end
    cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
}

こんな感じでURLに意味のない文字列_=1445733432387を付与していることで一意にすることでキャッシュ無しを実現している。 が、POSTやDELETEにcacheオプションを設定してもエラーにならないが、POSTやDELETEのときに飛んでいるリクエストを見ると付与されないことがわかる。

POSTは元々平気なのでDELETEのときだけキャッシュの問題を踏んでしまい、先ほどのように動作しない結果となる。

実際にDELETEのリクエストURLに'&_='+Math.floor($.now()/1000))を付与したところ正しく動作した。

まとめ

ひとまず自分が行った検証結果では

  • 標準ブラウザとWebViewの違いは見られない
  • JSONPやPOSTは問題無し
  • GETやDELETEはキャッシュのコントロールに気をつければ2回目の通信は可能

となった*3

*1:Android2.3のシェアはDashboards | Android Developersによると3.8%

*2:to-R AndroidiPhoneのHTML,CSS,JavaScriptのバグまとめ http://blog.webcreativepark.net/2012/03/13-093853.htmlは特に役立つ

*3:よく考えるとサーバ側はPOST/DELETEは更新なのでレスポンスをno-cacheで返すのが正しいやり方なのだろうか