Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
MRAID広告の実装から見るWebViewとアプリ間のインタラクション実装
Search
k-tomoyasu
September 25, 2025
Programming
0
74
MRAID広告の実装から見るWebViewとアプリ間のインタラクション実装
2025/09/24開催のAfter DroidKaigi 2025 from ピクシブでの発表資料です。
https://pixiv.connpass.com/event/363493/
k-tomoyasu
September 25, 2025
Tweet
Share
More Decks by k-tomoyasu
See All by k-tomoyasu
Coroutinesを中心としたAndroidアプリでの並行数制限・排他制御
fusuma0325
0
210
Android Studioプラグインを作ってみよう 〜Compose for Desktopで始めるプラグイン開発〜
fusuma0325
1
740
Kotlin Multiplatform Projectで社内用APIクライアントを作る
fusuma0325
0
2.1k
Kotlin/Nativeで作ってみるCLI, iOSアプリ
fusuma0325
1
140
Redashアラートの最近 - カスタマイズ機能を作った話
fusuma0325
0
840
Other Decks in Programming
See All in Programming
re:Invent 2025 トレンドからみる製品開発への AI Agent 活用
yoskoh
0
570
CSC307 Lecture 02
javiergs
PRO
1
740
【卒業研究】会話ログ分析によるユーザーごとの関心に応じた話題提案手法
momok47
0
160
20251212 AI 時代的 Legacy Code 營救術 2025 WebConf
mouson
0
240
Go コードベースの構成と AI コンテキスト定義
andpad
0
150
Implementation Patterns
denyspoltorak
0
140
「コードは上から下へ読むのが一番」と思った時に、思い出してほしい話
panda728
PRO
39
26k
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
580
Giselleで作るAI QAアシスタント 〜 Pull Requestレビューに継続的QAを
codenote
0
330
PostgreSQLで手軽にDuckDBを使う!DuckDB&pg_duckdb入門/osc25hi-duckdb
takahashiikki
0
230
まだ間に合う!Claude Code元年をふりかえる
nogu66
5
920
0→1 フロントエンド開発 Tips🚀 #レバテックMeetup
bengo4com
0
450
Featured
See All Featured
Marketing to machines
jonoalderson
1
4.5k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
210
Deep Space Network (abreviated)
tonyrice
0
32
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
9.8k
Chasing Engaging Ingredients in Design
codingconduct
0
93
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
0
3.4k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
120
Docker and Python
trallard
47
3.7k
Faster Mobile Websites
deanohume
310
31k
The Cost Of JavaScript in 2023
addyosmani
55
9.4k
The Power of CSS Pseudo Elements
geoffreycrofte
80
6.1k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Transcript
After DroidKaigi 2025 MRAID広告の実装から見るWebView とアプリ間のインタラクション実装 fusuma Comic Division pixivコミックSection プロダクト開発Unit
開発Team
fusuma 2021年中途入社 pixivコミック Androidアプリ開発
What’s MRAID • ネイティブアプリの広告SDKを利用していると目にすることがある ◦ SDKのドキュメントだったり、 Logcatに出力されていたり • Mobile Rich
Media Ad Interface Difinitions (MRAID) ◦ IABが策定したモバイルアプリ内のリッチメディア広告に関する規格 • WebViewで実装された広告とネイティブアプリを連携 ◦ スクロールで広告が画面外となったら広告の音を止める ◦ 広告内の要素をタップしたら広告のサイズを変更する、カレンダーを起動する等
What’s MRAID • JavaScript側でMRAIDのAPIを呼び出すことで広告がインタラクティブな挙動 をする • WebView上のhtml/javascriptだけでは実現できない ◦ 広告が見えなくなったら広告の音声を止める →
アプリの画面上のどの位置に WebView(広告)があるか分からない ◦ WebView(広告)内の要素タップでカレンダー起動 → Intentを投げる必要がある
今日のトピック • ネイティブアプリ <-> WebViewで相互に通信する方法 ◦ ネイティブアプリ -> WebView ◦
WebView -> ネイティブアプリ ▪ addJavascriptInterface方式 ▪ カスタムURLスキーマ方式
ネイティブアプリ → WebView • WebView#evaluateJavascript ◦ webView.evaluateJavascript("{script}", null) で渡したスクリプトが WebView上で実行さ
れる ◦ 第二引数(オプショナル)で実行結果を受け取るコールバックの設定 ◦ API Level 19未満はwebView.loadUrl("javascript:{script}")で実現できる • iOSでもWKWebView#evaluateJavaScriptで同等の機能が提供される
WebView → ネイティブアプリ • addJavascriptInterface方式 • カスタムURLスキーマ方式
addJavascriptInterface方式 • JavaScriptからネイティブアプリ側の処理を実行する代表的な手段 • ネイティブアプリ側の実装をJavaScript側に公開する • WebView#addJavascriptInterface ◦ 第一引数に公開したいメソッドを実装したクラスのインスタンス ◦
第二引数にJavaScript上でのオブジェクト名
addJavascriptInterfaceの実装
None
class WebAppInterface(private val context: Context) { // 公開するメソッドにアノテーションを付与 @JavascriptInterface fun
showToast(message: String) { // ※JavaScriptからの呼び出しはUIスレッドではないことに注意 Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } } val webview = WebView(context) // JavaScriptを有効にする webview.settings.javaScriptEnabled = true // クラスのインスタンスを "Android" という名前で登録 webview.addJavascriptInterface(WebAppInterface(context), "Android") Kotlin
<body> <button onclick="sendToApp('From WebView!')">Show Toast</button> <script> function sendToApp(message) { Android.showToast(message);
} </script> </body> JavaScript
カスタムURLスキーマ方式 • MRAIDの実装でみられるパターン • URL遷移をフックするWebViewClient#shouldOverrideUrlLoadingを活用 • WebViewのURL遷移をネイティブアプリ側の処理のトリガーにする ◦ 実行したい処理をURLで表現してアプリ側に渡す ◦
WebViewClient#shouldOverrideUrlLoadingでURLを検証してネイティブアプリ側の処理を実行
shouldOverrideUrlLoadingを用いた処理の流れ 1. WebView側でmraid://…というスキーマでURL遷移する 2. 遷移時にshouldOverrideUrlLoadingの引数に1.のURLが渡される ここでネイティブアプリ側の処理がトリガー 3. URL検証 ◦ `mraid`というカスタムURLスキーマでMRAIDの処理と判断できる
◦ ホストが`resize` ならresizeを実行する ◦ クエリパラメータからresize時のパラメータを決定 mraid://resize?w=...&h=...&... トリガー コマンド パラメーター
カスタムURLスキーマ方式の実装
<body> <button onclick="sendToApp('From WebView!')">Show Toast</button> <script> function sendToApp(message) { const
encodedMessage = encodeURIComponent(message); // カスタムURLへ遷移させる (遷移がトリガーとなるので<a>タグでも動作する) window.location.href = `myapp://toast?message=${encodedMessage}`; } </script> </body> JavaScript
val webview = WebView(context).also { it.settings.javaScriptEnabled = true } webview.webViewClient
= object : WebViewClient() { override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest ): Boolean { val url = request.url // スキーマ(myapp)とホスト(toast)をチェック if (url.scheme == "myapp" && url.host == "toast") { // クエリパラメータ"message"を取得 val message = url.getQueryParameter("message") if (!message.isNullOrEmpty()) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } return true // WebViewによる読み込みをキャンセル } return false } Kotlin
戻り値の有無 • addJavascriptInterface方式ではJavaScript側はアプリ側のメソッドから直 接戻り値を受け取れる • カスタムURLスキーマ方式では一手間かけて間接的に値を渡す ◦ アプリ側はshouldOverrideUrlLoadingでURLで渡された処理を行い、 evaluateJavascript でJavaScript側に値をセット
実装の容易さ • 総合的にはaddJavascriptInterface • カスタムURLスキーマはAndroid・iOS両方で利用できるのが特徴 ◦ iOSはWKNavigationDelegate#webView(_:decidePolicyFor:decisionHandler:)でURL 遷移をインターセプトする ◦ JavaScript側はどちらのプラットフォームでも同じ
URL遷移を行えばよい ▪ (JavaScript側では) プラットフォームごとの実装が減る
セキュリティ • WebViewからアプリのコードを実行する穴を開けてる点は共通 ◦ いずれも読み込むWebページの信頼性が重要 ▪ 自身で管理するWebページ以外で利用しないことが推奨される ▪ 意図しないWebページからアプリのコードを実行されるリスクを回避
MessageChannel
MessageChannel • API Level 23以降から利用できる ◦ addJavascriptInterface・shouldOverrideUrlLoadingはAPI Level 1から利用可能 •
HTML5のmessage portsのWebView実装 ◦ https://developer.android.com/reference/android/webkit/WebMessagePort • ネイティブアプリ側と JavaScript側で相互にメッセージを送り合える • 送信するoriginを制限することで意図しない Webページからアプリのコードが実行されるリスクを緩 和できる • Webの標準仕様に乗っかれる
val webView = WebView(context) // JavaScriptを有効にする webView.settings.javaScriptEnabled = true val
channel = webview.createWebMessageChannel() val nativePort = channel[0] val webViewPort = channel[1] // JavaScriptからのメッセージを受け取るコールバックを設定する nativePort.setWebMessageCallback(object : WebMessagePort.WebMessageCallback() { override fun onMessage(port: WebMessagePort, message: WebMessage?) { val messageText = message?.data ?: return Toast.makeText(context, messageText, Toast.LENGTH_SHORT).show() } }) webView.webViewClient = object : WebViewClient() { // WebViewがページを読み込み終わったら portをJavaScriptに送る override fun onPageFinished(view: WebView, url: String?) { // 第二引数のtargetOriginで送信先のoriginを制限することでセキュリティリスクを緩和 // とりあえず動かすだけなら Uri.parse(“*”)などを指定する。 view.postWebMessage( WebMessage("port_setup", arrayOf(webViewPort)), Uri.parse("https://foo.example.com") ) } Kotlin
<body> <button onclick="sendToWebView('From WebView!')">Show Toast</button> <script> let appPort; // 送られたportを受け取る
window.addEventListener('message', event => { if (event.data === 'port_setup') { appPort = event.ports[0]; appPort.onmessage = (event) => { // ネイティブから送られたメッセージを受け取って処理 }; } }); function sendToApp(message) { // アプリ側にメッセージ送信 appPort.postMessage(message); } </script> </body> JavaScript
まとめ • WebViewにはネイティブアプリと相互に通信する方法がある • WebView -> ネイティブアプリ方向の通信は複数の手法がある ◦ 代表的なaddJavaScriptInterfaece、MRAID実装でみられるカスタム URLスキーマ、API
Level23以降で使えるMessageChannel ◦ 多くの場合は自身の Webページしか読み込まないようにして addJavaScriptInterfaceを使う のが簡単 • WebViewの可能性が広がる。が、採用は慎重に
参考 • Android Developers ◦ WebViewでwebアプリを開発する ▪ https://developer.android.com/develop/ui/views/layout/webapps/webview ◦ ネイティブブリッジのリスク
▪ https://developer.android.com/privacy-and-security/risks/insecure-webview-native-brid ges • Appnexus ◦ MRAID実装 ▪ https://github.com/appnexus/mobile-sdk-android/