Upgrade to Pro — share decks privately, control downloads, hide ads and more …

2020年代の WebView 実装 / saikou_no_webview_2021

2020年代の WebView 実装 / saikou_no_webview_2021

DroidKaigi 2021 で発表
https://www.youtube.com/watch?v=IOnpHyOg5sc

長期的にメンテナンス可能な Android アプリの WebView 画面実装について発表しました。

こやまカニ大好き

October 22, 2021
Tweet

More Decks by こやまカニ大好き

Other Decks in Programming

Transcript

  1. WebView でないとダメなケース 外部ブラウザや CustomTabs では実現できないケースが存在する • 画面内のコンテンツの一部として WebView を利用している •

    ウェブコンテンツからネイティブ画面への遷移が多い • ネイティブコードの呼び出しを実行したい
  2. 例外 : 認証系の処理 アプリのセッションを引き継いでWebコンテンツを表示したい場合 以下のような方法で実装する場合は CustomTabs でも実装可能 • 認証情報をURLに含める場合 •

    認証情報をリクエストヘッダに含める場合 ◦ Chrome側の実装により制限あり ◦ https://developer.chrome.com/docs/android/custom-tabs/headers/
  3. WebView の歴史(1/3) • 有史以前 ◦ WebKit ベースのシステム組み込み WebView コンポーネント ◦

    OSを更新しなければアップデートされない ◦ 同じOSバージョンでもメーカーごとに挙動が違ったりする • Android 4.4 ◦ Chromium(Blink) ベースの WebView コンポーネント ◦ 挙動はかなりまともになった ◦ OSを更新しなければアップデートされない
  4. WebView の歴史(2/3) • Android 5.0 ◦ WebView コンポーネントが独立してアップデートできるようになった ◦ 外部ブラウザと分離されたため、履歴やブックマークの同期機能が無くなった

    ◦ 基本的に Android 5.0 以降であれば WebView を利用する上で大きな問題はない ◦ WebView を多用するアプリなら最低でも minSdkVersion 21 にしておいたほうが良い • Android 7.0 ◦ Chrome アプリを利用してレンダリングされるようになった ◦ Chrome Beta や Dev 版をレンダリングに利用できるようになった
  5. WebView の歴史(3/3) • Android 10 ◦ 再び WebView コンポーネントアプリを利用するようになった ◦

    WebView コンポーネントアプリにも Beta, Dev がそれぞれ存在する
  6. 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
  7. WebView 誕生編 クックパッドの Android アプリは 2012 年リリース リリース当初は完全な WebView アプ

    リとして実装されていた(らしい) 翌年ネイティブアプリとして再実装さ れ、当初の WebView は CustomWebView として残った
  8. カオスなカスタム JavaScript • 正しい実装なのかわからない 45 個のメソッド • それぞれの利用頻度や利用ページも一切分からない • 認証情報を一時的に

    WebViewFragment に持たせる危険な仕組み • JavaScript からほぼ直接 SharedPreference に読み書きさせる処理 ◦ getStorage(key), setStorage(key, value), getStorageKeys() などなど • アプリを終了させる finishApplication() メソッド ◦ 実際には Activity.finish() を呼ぶ ◦ 別Activityでの WebView 表示では正しく動いていない
  9. 全部 Activity/Fragment にベタ書きされていて辛い • 各インターフェイスが Activity/Fragment に直接実装されている ◦ 最初の WebViewFragment

    がその状態で、そこから全部コピペされている ◦ WebViewFragment の更新にコピペ先が追従しないので差分が生まれていく • ライフサイクル管理、ダイアログなどの共通処理が全てのWebView画面にほぼそ のままコピペされている ◦ 300行くらいは全ての WebView で必要としている共通処理 ◦ 一部は Kotlin 化されていたりして微妙〜に違う
  10. 2020年代の WebView 10年戦える最高の WebView を作りたい • カスタムJavaScriptが理解可能 • 他の画面と同じアーキテクチャで実装されている •

    共通実装は一カ所にまとまっていてコピペしなくても利用できる • 利用箇所ごとにカスタマイズ可能
  11. カオスなカスタム JavaScript の改善 • JavaScriptメソッドにステージの概念を追加 ◦ 利用可能 : 名前の通り利用可能 ◦

    非推奨 : ネイティブ側の実装に何らかの問題があり廃止したい ◦ 廃止 : JSメソッドは存在するがネイティブ側の実装が削除済み ◦ ネイティブ処理呼び出しのための callback interface も分割 • すべてのメソッドの呼び出しにログ追加 • 一定期間呼び出しがないメソッドは降格 • 未整理45の状態から利用可能 10、非推奨 2 まで削減できた
  12. クックパッドアプリはVIPERアーキテクチャ • View : 画面処理 ◦ ダイアログ表示、ライフサイクルの管理 • Interactor :

    ビジネスロジック ◦ セッション引き継ぎ用トークンの取得処理など • Presenter : View とのやりとりや他のコンポーネントの処理呼び出し ◦ URLチェックやカスタムJavaScriptは Presenter だけ知っていれば良い状態が理想 • Routing : 画面遷移 ◦ ネイティブ画面への遷移はここに集約 他の画面と同じアーキテクチャで実装されている
  13. WebView の VIPER アーキテクチャ Fragment View Presenter Interactor Routing WebView

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

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

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

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

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

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

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

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

    個人的に VIPER が他のモバイル アプリアーキテクチャと比べて優れ ているところは Routing interface に画面遷移をすべて定義している ところだと思います。 これは WebView のような画面遷 移が複雑化しやすい画面では特に 有効で、パラメータが異なる画面遷 移や今回のような実装クラスによっ て挙動が大きく変わるような仕様も きちんと定義できるのが便利だと 思っています。 クックパッドアプリでは Routing ク ラスで画面遷移を処理するための 便利な仕組みをいくつか用意して いますが、時間が無いのでまた別 の機会に話します。
  22. 利用箇所ごとにカスタマイズ可能 WebView 画面の増殖は避けられない Interface でカスタマイズ可能な箇所を定義して クックパッドアプリで画面によってカスタマイズしたい箇所 • ログイン、ログアウトなど認証系の処理 ◦ 認証用途で実装している

    WebView 以外では不要 • カスタムJavaScript ◦ 一部の画面でのみ追加したい JavaScript メソッド • ネイティブ画面への遷移 ◦ 別Activityで WebView を表示する場合はサポートしない、等
  23. ExternalWebView Contract カスタマイズ可能な Contract の分離 Routing Interactor Presenter View BaseWebView

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

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

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

    CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承
  27. 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 で実装している
  28. 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 のコン ポーネントをそれぞれ継承し、足りないメソッド を実装する
  29. GooglePlaySubscription WebView VIPER Scene カスタマイズ可能な WebView の実装 BaseWebView VIPER Scene

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

    CustomWebView BaseRouting BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 最高の WebView できた
  31. GooglePlaySubscription WebView VIPER Scene クックパッドアプリの現状 BaseWebView VIPER Scene CustomWebView BaseRouting

    BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承
  32. 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 が薄くなっていて明確 に分離できていない
  33. GooglePlaySubscription WebView VIPER Scene クックパッドアプリの現状 BaseWebView VIPER Scene CustomWebView BaseRouting

    BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 特定の WebView 画面の実装だけコード量が異 常 に多い 新しいWebViewアーキテクチャでは VIPER シー ンを分割すべきところを無理矢理ひとつの WebViewFragment に押し込めていた名残
  34. 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 だけ意識して実装で きるようにしたいが、うまくアーキテクチャに落とし 込めていない
  35. GooglePlaySubscription WebView VIPER Scene クックパッドアプリの現状 BaseWebView VIPER Scene CustomWebView BaseRouting

    BaseInteractor BasePresenter BaseView WebView VIPER Scene Routing Interactor Presenter View 継承 最高の WebView まだできてない!!!