Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
MRAID広告の実装から見るWebViewとアプリ間のインタラクション実装
Search
k-tomoyasu
September 25, 2025
Programming
0
71
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
170
Android Studioプラグインを作ってみよう 〜Compose for Desktopで始めるプラグイン開発〜
fusuma0325
1
660
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
tsgolintはいかにしてtypescript-goの非公開APIを呼び出しているのか
syumai
6
2.1k
ZOZOにおけるAI活用の現在 ~モバイルアプリ開発でのAI活用状況と事例~
zozotech
PRO
8
5.4k
愛される翻訳の秘訣
kishikawakatsumi
1
300
社内オペレーション改善のためのTypeScript / TSKaigi Hokuriku 2025
dachi023
1
550
DSPy Meetup Tokyo #1 - はじめてのDSPy
masahiro_nishimi
1
160
WebRTC と Rust と8K 60fps
tnoho
2
1.9k
AIコーディングエージェント(Gemini)
kondai24
0
190
Full-Cycle Reactivity in Angular: SignalStore mit Signal Forms und Resources
manfredsteyer
PRO
0
120
生成AIを利用するだけでなく、投資できる組織へ
pospome
0
160
テストやOSS開発に役立つSetup PHP Action
matsuo_atsushi
0
150
TypeScriptで設計する 堅牢さとUXを両立した非同期ワークフローの実現
moeka__c
6
3k
関数実行の裏側では何が起きているのか?
minop1205
1
670
Featured
See All Featured
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
231
22k
Building an army of robots
kneath
306
46k
Java REST API Framework Comparison - PWX 2021
mraible
34
9k
Build The Right Thing And Hit Your Dates
maggiecrowley
38
3k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.1k
Reflections from 52 weeks, 52 projects
jeffersonlam
355
21k
It's Worth the Effort
3n
187
29k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
7.8k
Balancing Empowerment & Direction
lara
5
790
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.7k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
970
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/