How WebView Sucks

WebView のダメさというのは広く語られて久しいが、自分も仕事でさわった WebView を通じて理解が深まった。周回遅れは承知で記録しておく。なお自分の仕事アプリは電子書籍リーダであり電子書籍の標準フォーマットは HTML なので WebView を使うのは仕方ない。以下は仕事に文句を言ってるわけではない。

モバイル向けのサービスを作りにあたり、数年前まではアプリを WebView で実装しようとする人々がいた。そして滅びた。彼らは Web テクノロジのダメさを非難したけれど、Web のダメさは理由の半分くらいだと思う。残りの半分は WebView のダメさではないか。少なくとも Android では。

Android の WebView は Web と Android のダメなところを集めた代物といえる。

まず WebView は View のサブクラスなので Activity のライフサイクルに引きずられて作られたり破棄されたりする。しかし WebView の中からライフサイクルイベントを知ることはできないし状態も直列化されない。結果としてライフサイクルが切り替わるたびにページが再構築される。状態がふっとんでしまうし、遅い。様々なものが View の寿命にひきずられるのはほんとにキツイ。ライフサイクルの遷移による WebView の再構築はウェブページのリロード相当ではない。どちらかというとブラウザの再起動みたいなもの。レンダリングエンジンの使う OS の資源は一旦破棄したあと確保しなおされる。キャッシュも全部消える。超遅い。

同じモバイルでも WebView でなくブラウザのウェブならこうした問題はおきない。ブラウザはページを Service にホストするため Activity ライフサイクルの影響を受けない。仮にブラウザが背後のタブにいるページを一旦 purge したとしてもブラウザ本体は生き続けている。だからタブの開き直しは WebView つくりなおしより速い。

画面の描画もきびしい。最近の WebView は別スレッドの GL でページを書いて結果を SurfaceView としてアプリ本体の View ツリーに composite するけれど、メインスレッドを自分でもっていないせいでブラウザがやっている小細工がまったくできない。遅い。

スレッドといえば、アプリのスレッドと WebView のスレッドがわかれておりイベントループが二つあるのもつらい。普通の WebView のユースケースでウェブのイベントループとアプリのイベントループを混ぜたくないのはわかる。ランダムなウェブサイトの遅さがアプリを止めてしまうから。でもその理不尽な環境下にある WebView を主体としてアプリをつくるとか、無理でしょ。

このスレッドモデルのせいで WebView と Java のブリッジコールは超遅い。ブリッジは WebView のスレッドでもなくメインループでもない謎のスレッドで呼ばれる。スレッドホップが挟まるせいで JS が 1ms 近く止まってしまう。しかもブロッキングせずに ブリッジを呼ぶ方法がない。最近は postMessage() できるようになったけど。いずれにせよ JS の世界と Java の世界は大きく隔たっている。これは Node を自由に使える Electron とはだいぶ違うし、ActiveX の使えた Windows の IE component よりもしょぼい。

こういう技術的な問題のせいで WebView ベースのアプリは Android の力をまったく引き出せない。そのくせウェブのよさも全部捨ててしまっている。ブラウザの資源を使えない。URL もない。Hyperlink もない。柔軟なデプロイもできない。検索エンジンからもソーシャルメディアからも traffic を呼び込めない。そりゃダメだろ・・・

今となってはブラウザもマシになったし JS で書きたいだけなら React Native もある。WebView で頑張ろうとする人はいない。だからこの話は歴史的な回顧でしかない。Lesson Learned があるとすれば何か。Web やその他のテクノロジをプレットホーム抽象化レイヤとして使ってはいけないということだろう。Web の良さはプラットフォーム中立であること(だけ)ではない。Web は Web というプラットホームであり、独立したプラットホームとしての良さがある。その力を引き出さないと良いものは作れない。WebView はそれを反例として示した。