Slide 1

Slide 1 text

2020年代の WebView 実装 こやまカニ大好き

Slide 2

Slide 2 text

自己紹介 こやまカニ大好き クックパッド モバイル基盤部 所属 モバイルアプリ開発の生産性を向上す るようなタスクに従事 最近は WebView 大好き

Slide 3

Slide 3 text

Agenda WebView とは WebView の基礎知識 クックパッドアプリとWebView 2020年代の WebView 最後に 01 02 03 04 05

Slide 4

Slide 4 text

Agenda WebView とは WebView の基礎知識 クックパッドアプリと WebView 2020年代の WebView 最後に 01 02 03 04 05

Slide 5

Slide 5 text

WebView とは ウェブページをレンダリングするためのView 通常のViewと比べて以下の利点がある(と思う) ● ウェブサービスの場合、低コストでコンテンツを表示可能 ● 表示するコンテンツの変更をリリースを介さずに行うことができる ● 動画や音楽再生などのリッチコンテンツを扱う機能が実装済み

Slide 6

Slide 6 text

WebView 以外の方法 ウェブページの表示自体は Chrome な どの外部ブラウザに移譲できる Android では AndroidX Browser の CustomTabs を利用したアプリと親和 性の高いブラウザ表示も可能

Slide 7

Slide 7 text

WebView でないとダメなケース 外部ブラウザや CustomTabs では実現できないケースが存在する ● 画面内のコンテンツの一部として WebView を利用している ● ウェブコンテンツからネイティブ画面への遷移が多い ● ネイティブコードの呼び出しを実行したい

Slide 8

Slide 8 text

ケース #1 コンテンツの一部として WebView を利用している ボトムタブや ViewPager の一部として ウェブコンテンツを表示したい場合、 WebView を利用するしかありません。 タブの中に表示している

Slide 9

Slide 9 text

ケース #2 ウェブコンテンツからネイ ティブ画面への遷移 外部ブラウザ と 自アプリのネイティブ 画面を行き来するのはActivity同士の 遷移になるため制限が多く、ユーザー 体験を損なう 押下するとログイン画面に遷移する

Slide 10

Slide 10 text

ケース #3 ネイティブコードの呼び出し カスタムJavaScript や独自スキーム のURLフック等を通じてネイティブ実装 された処理を呼び出したい場合がある ● ユーザー情報の更新 ● GooglePlay決済処理の実行 決済関連の処理を呼び出す

Slide 11

Slide 11 text

例外 : 認証系の処理 アプリのセッションを引き継いでWebコンテンツを表示したい場合 以下のような方法で実装する場合は CustomTabs でも実装可能 ● 認証情報をURLに含める場合 ● 認証情報をリクエストヘッダに含める場合 ○ Chrome側の実装により制限あり ○ https://developer.chrome.com/docs/android/custom-tabs/headers/

Slide 12

Slide 12 text

Agenda WebView とは WebView の基礎知識 クックパッドアプリと WebView 2020年代の WebView 最後に 01 02 03 04 05

Slide 13

Slide 13 text

WebView の歴史(1/3) ● 有史以前 ○ WebKit ベースのシステム組み込み WebView コンポーネント ○ OSを更新しなければアップデートされない ○ 同じOSバージョンでもメーカーごとに挙動が違ったりする ● Android 4.4 ○ Chromium(Blink) ベースの WebView コンポーネント ○ 挙動はかなりまともになった ○ OSを更新しなければアップデートされない

Slide 14

Slide 14 text

WebView の歴史(2/3) ● Android 5.0 ○ WebView コンポーネントが独立してアップデートできるようになった ○ 外部ブラウザと分離されたため、履歴やブックマークの同期機能が無くなった ○ 基本的に Android 5.0 以降であれば WebView を利用する上で大きな問題はない ○ WebView を多用するアプリなら最低でも minSdkVersion 21 にしておいたほうが良い ● Android 7.0 ○ Chrome アプリを利用してレンダリングされるようになった ○ Chrome Beta や Dev 版をレンダリングに利用できるようになった

Slide 15

Slide 15 text

WebView の歴史(3/3) ● Android 10 ○ 再び WebView コンポーネントアプリを利用するようになった ○ WebView コンポーネントアプリにも Beta, Dev がそれぞれ存在する

Slide 16

Slide 16 text

WebView 実装を行う上での注意点 ● minSdkVersion ● API レベルごとの機能差異 ● WebView のライフサイクル ● WebView 起因の障害

Slide 17

Slide 17 text

minSdkVersion ● できれば Android 6.0 (API 23) 以上 ○ WebView というよりも RuntimePermission の都合 ○ WebView 内で Permission を扱う処理がある場合、ある程度実装を揃えられる ● 最低でも Android 5.0 (API 21) 以上 ○ すべての端末で WebView の更新が期待できる ○ 問題があった場合は Chromium のバグトラッカーを見れば良いので分かり易い ○ https://bugs.chromium.org/p/chromium/issues/list

Slide 18

Slide 18 text

WebView のライフサイクル管理 ● Fragment/Activity のライフサイクルに沿っていくつかのメソッドを呼び出す必要 がある ○ onPause(), onResume() など ● バックグラウンドで動画再生が続いてしまうとリジェクトされる可能性がある

Slide 19

Slide 19 text

Agenda WebView とは WebView の基礎知識 クックパッドアプリと WebView 2020年代の WebView 最後に 01 02 03 04 05

Slide 20

Slide 20 text

WebView 誕生編 クックパッドの Android アプリは 2012 年リリース リリース当初は完全な WebView アプ リとして実装されていた(らしい) 翌年ネイティブアプリとして再実装さ れ、当初の WebView は CustomWebView として残った

Slide 21

Slide 21 text

WebView 成長編 ネイティブアプリ後も引き続きコンテンツのいく つかは WebView 表示 ネイティブ画面が増えたことで画面遷移やカス タムJavaScript実装が急増 2018年時点で CustomWebView 841行 WebViewFragment 1335 行 WebView 関連コードはおよそ 3000行

Slide 22

Slide 22 text

WebView 増殖編(1) 2018年  GooglePlay 定期購入機能の追加 WebViewで表示するランディングページから GooglePlay決済を呼び出す必要があった 「既存のWebViewは触りたくない」 「別Activityで表示する決済機能だけのシンプル なWebViewを作ろう」 GooglePlaySubscriptionWebViewA ctivity 爆誕

Slide 23

Slide 23 text

2019 年  ViewPager 内に表示するWebViewに GooglePlay 決済機能を追加 「既存のWebViewは触りたくない」 「決済機能だけのシンプルなWebView を作ろう」 「決済ページ以外は通常WebViewに遷移させよう」 GooglePlaySubscriptionWebVie wContainerFragment 爆誕 WebView 増殖編(2)

Slide 24

Slide 24 text

その後も成長と増殖を繰り返した WebView 2021年8月時点で Activity 2つ、 Fragment が 4つ存在 WebView 関連コードの総量はおよそ 5000行(ほぼ全てJava) WebView 2021・夏

Slide 25

Slide 25 text

見えてきた課題 ● カオスなカスタム JavaScript ● 各 WebView 画面がサポートする機能がわからない ● 全部 Activity/Fragment にベタ書きされていて辛い

Slide 26

Slide 26 text

カオスなカスタム JavaScript ● 正しい実装なのかわからない 45 個のメソッド ● それぞれの利用頻度や利用ページも一切分からない ● 認証情報を一時的に WebViewFragment に持たせる危険な仕組み ● JavaScript からほぼ直接 SharedPreference に読み書きさせる処理 ○ getStorage(key), setStorage(key, value), getStorageKeys() などなど ● アプリを終了させる finishApplication() メソッド ○ 実際には Activity.finish() を呼ぶ ○ 別Activityでの WebView 表示では正しく動いていない

Slide 27

Slide 27 text

各WebView画面がサポートする機能がわからない インターフェイスの整備が中途半端なため、実装の有無を返せるメソッドと返せないメ ソッドがある boolean で実装の有無を返せる boolean で実装の有無を返せない

Slide 28

Slide 28 text

全部 Activity/Fragment にベタ書きされていて辛い ● 各インターフェイスが Activity/Fragment に直接実装されている ○ 最初の WebViewFragment がその状態で、そこから全部コピペされている ○ WebViewFragment の更新にコピペ先が追従しないので差分が生まれていく ● ライフサイクル管理、ダイアログなどの共通処理が全てのWebView画面にほぼそ のままコピペされている ○ 300行くらいは全ての WebView で必要としている共通処理 ○ 一部は Kotlin 化されていたりして微妙〜に違う

Slide 29

Slide 29 text

2010 年代 WebView の限界 最初の実装から8年維持できたのは本当にすごい でもさすがにもうメンテナンスできない WebView の機能を整理したい

Slide 30

Slide 30 text

Agenda WebView とは WebView の基礎知識 クックパッドアプリと WebView 2020年代の WebView 最後に 01 02 03 04 05

Slide 31

Slide 31 text

2020年代の WebView 10年戦える最高の WebView を作りたい ● カスタムJavaScriptが理解可能 ● 他の画面と同じアーキテクチャで実装されている ● 共通実装は一カ所にまとまっていてコピペしなくても利用できる ● 利用箇所ごとにカスタマイズ可能

Slide 32

Slide 32 text

カオスなカスタム JavaScript の改善 ● JavaScriptメソッドにステージの概念を追加 ○ 利用可能 : 名前の通り利用可能 ○ 非推奨 : ネイティブ側の実装に何らかの問題があり廃止したい ○ 廃止 : JSメソッドは存在するがネイティブ側の実装が削除済み ○ ネイティブ処理呼び出しのための callback interface も分割 ● すべてのメソッドの呼び出しにログ追加 ● 一定期間呼び出しがないメソッドは降格 ● 未整理45の状態から利用可能 10、非推奨 2 まで削減できた

Slide 33

Slide 33 text

クックパッドアプリはVIPERアーキテクチャ ● View : 画面処理 ○ ダイアログ表示、ライフサイクルの管理 ● Interactor : ビジネスロジック ○ セッション引き継ぎ用トークンの取得処理など ● Presenter : View とのやりとりや他のコンポーネントの処理呼び出し ○ URLチェックやカスタムJavaScriptは Presenter だけ知っていれば良い状態が理想 ● Routing : 画面遷移 ○ ネイティブ画面への遷移はここに集約 他の画面と同じアーキテクチャで実装されている

Slide 34

Slide 34 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView

Slide 35

Slide 35 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView WebView は 内部にシステムから用意さ れているコールバックを実装する それぞれのイベントと対応した Presenter のメソッドを呼び出す

Slide 36

Slide 36 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView Presenter は呼び出されたメソッドに応じ て他のコンポーネントの実装を呼び出す

Slide 37

Slide 37 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView Presenter は呼び出されたメソッドに応じ て Routing や Interactor の実装を呼び 出す

Slide 38

Slide 38 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView View はダイアログやページ読み込み中 の Progress の管理を行っている WebView の操作が必要な場合は必ず View を経由する

Slide 39

Slide 39 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView View はダイアログやページ読み込み中 の Progress の管理を行っている WebView の操作が必要な場合は必ず View を経由する

Slide 40

Slide 40 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView Interactor はセッション引き継ぎが 必要なURLにアクセスした際の認 証処理などを実装している

Slide 41

Slide 41 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView Routing は主にネイティブ画面への遷移 を定義している 「このWebViewではこのネイティブ画面 への遷移はサポートしない」という定義 ができるインターフェイスになっている

Slide 42

Slide 42 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView Routing は主にネイティブ画面への遷移 を定義している 「このWebViewではこのネイティブ画面 への遷移はサポートしない」という定義 ができるインターフェイスになっている

Slide 43

Slide 43 text

WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView 個人的に VIPER が他のモバイル アプリアーキテクチャと比べて優れ ているところは Routing interface に画面遷移をすべて定義している ところだと思います。 これは WebView のような画面遷 移が複雑化しやすい画面では特に 有効で、パラメータが異なる画面遷 移や今回のような実装クラスによっ て挙動が大きく変わるような仕様も きちんと定義できるのが便利だと 思っています。 クックパッドアプリでは Routing ク ラスで画面遷移を処理するための 便利な仕組みをいくつか用意して いますが、時間が無いのでまた別 の機会に話します。

Slide 44

Slide 44 text

共通実装がまとまっている 共通実装が多いのでまとめたい ● WebViewのライフサイクル管理 ● ファイル選択、ダイアログ表示などの基本UI ● 特定ドメインのURLにアクセスした際のセッション引き継ぎ処理 ● どのWebViewでも利用する基本的な画面遷移 ○ 外部ブラウザや外部メーラーアプリへの遷移など

Slide 45

Slide 45 text

利用箇所ごとにカスタマイズ可能 WebView 画面の増殖は避けられない Interface でカスタマイズ可能な箇所を定義して クックパッドアプリで画面によってカスタマイズしたい箇所 ● ログイン、ログアウトなど認証系の処理 ○ 認証用途で実装している WebView 以外では不要 ● カスタムJavaScript ○ 一部の画面でのみ追加したい JavaScript メソッド ● ネイティブ画面への遷移 ○ 別Activityで WebView を表示する場合はサポートしない、等

Slide 46

Slide 46 text

ExternalWebView Contract カスタマイズ可能な Contract の分離 Routing Interactor Presenter View BaseWebView Contract Routing Interactor Presenter View 継承

Slide 47

Slide 47 text

ExternalWebView Contract カスタマイズ可能な Contract の分離 Routing Interactor Presenter View BaseWebView Contract Routing Interactor Presenter View 継承 WebView の表示箇所によるカスタマイズを 許す処理のみ External Contract に定義す る

Slide 48

Slide 48 text

ExternalWebView Contract カスタマイズ可能な Contract の分離 Routing Interactor Presenter View BaseWebView Contract Routing Interactor Presenter View 継承 すべての WebView で共通する処理は Base Contract に定義する

Slide 49

Slide 49 text

ExternalWebView Contract カスタマイズ可能な Contract の分離 Routing Interactor Presenter View BaseWebView Contract Routing Interactor Presenter View 継承 BaseContract のそれぞれのコンポーネント が 対応する ExternalContract を継承する ことで、 WebView や他のコンポーネントか らは BaseContract だけ参照すれば良い状 態になる

Slide 50

Slide 50 text

カスタマイズ可能な Contract の分離

Slide 51

Slide 51 text

カスタマイズ可能な Contract の分離 ここで ExternalContract.View を継承し て View を定義している

Slide 52

Slide 52 text

GooglePlaySubscription WebView VIPER Scene カスタマイズ可能な WebView の実装 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承

Slide 53

Slide 53 text

GooglePlaySubscription WebView VIPER Scene カスタマイズ可能な WebView の実装 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 共通実装 BaseWebView VIPER シーンのク ラスは全て abstract class ExternalContract 以外 のメソッドを すべて final で実装している

Slide 54

Slide 54 text

GooglePlaySubscription WebView VIPER Scene カスタマイズ可能な WebView の実装 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 各画面固有の WebView 実装(External Contract) 各画面固有の WebView 以外の実装 各 WebView 実装ではBaseWebView のコン ポーネントをそれぞれ継承し、足りないメソッド を実装する

Slide 55

Slide 55 text

実際のカスタマイズ例

Slide 56

Slide 56 text

実際のカスタマイズ例 ここで BaseWebViewRouting を継承している PsLandingPageWebViewContract.Routing は ActionBar など WebView 以外の 部分からの画面遷移をサポートするための定義が書かれた interface

Slide 57

Slide 57 text

実際のカスタマイズ例 このWebViewではこの画面遷移は未実装なので、 Unimplemented を返している 各画面遷移は BaseWebViewPresenter 内のメソッドで UnImplemented かどうか 判定され、ネイティブ画面に遷移するかウェブページとして読み込むかが変わる

Slide 58

Slide 58 text

GooglePlaySubscription WebView VIPER Scene カスタマイズ可能な WebView の実装 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 最高の WebView できた

Slide 59

Slide 59 text

Agenda WebView とは WebView の基礎知識 クックパッドアプリと WebView 2020年代の WebView 最後に 01 02 03 04 05

Slide 60

Slide 60 text

GooglePlaySubscription WebView VIPER Scene カスタマイズ可能な WebView の実装 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 最高の WebView できた

Slide 61

Slide 61 text

GooglePlaySubscription WebView VIPER Scene クックパッドアプリの現状 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承

Slide 62

Slide 62 text

GooglePlaySubscription WebView VIPER Scene クックパッドアプリの現状 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 WebViewがでかい。でかすぎる URL判定ロジックなど Presenter が持つべき実装 がまだ WebView に残っているせい その分 Presenter と View が薄くなっていて明確 に分離できていない

Slide 63

Slide 63 text

GooglePlaySubscription WebView VIPER Scene クックパッドアプリの現状 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 特定の WebView 画面の実装だけコード量が異 常 に多い 新しいWebViewアーキテクチャでは VIPER シー ンを分割すべきところを無理矢理ひとつの WebViewFragment に押し込めていた名残

Slide 64

Slide 64 text

GooglePlaySubscription WebView VIPER Scene クックパッドアプリの現状 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 ExternalWebView Contract を透過的に扱うため に BaseWebView Contract が ExternalWebView Contract を継承し、さらに abstract class として実 装する仕組みなっているが、 この構造だと ExternalWebView Contract の分離が十分ではな い 理想としては WebView 実装者が ExternalWebView Contract だけ意識して実装で きるようにしたいが、うまくアーキテクチャに落とし 込めていない

Slide 65

Slide 65 text

GooglePlaySubscription WebView VIPER Scene クックパッドアプリの現状 BaseWebView VIPER Scene CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 最高の WebView まだできてない!!!

Slide 66

Slide 66 text

まとめ 引き続き 10年戦える最高の WebView を目指して改善していきます クックパッドでは WebView 大好きな仲間を募集中 WebView が好きじゃない仲間でも扱える WebView を用意しているので WebView が好きじゃない仲間も募集中です!!!

Slide 67

Slide 67 text

WebView これだけはやっておけリスト ● WebViewでサポートする挙動を決めておく ○ ダイアログ、ファイル操作、音声入力などなど ● とにかくネイティブ画面への遷移を可視化できるようにする ● カスタムJavaScriptはできるだけ使わない方針にする ● カスタムJavaScriptを使う場合はメンテナンスできる仕組みを入れる ○ 全メソッドの発火ログ記録 ○ 非推奨メソッド可視化の仕組み おまけ

Slide 68

Slide 68 text

2020年代 の WebView ご静聴ありがとうございました。