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
77
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
Kotlin Notebookを プラグイン開発に活用する
fusuma0325
0
35
Coroutinesを中心としたAndroidアプリでの並行数制限・排他制御
fusuma0325
0
230
Android Studioプラグインを作ってみよう 〜Compose for Desktopで始めるプラグイン開発〜
fusuma0325
1
770
Kotlin Multiplatform Projectで社内用APIクライアントを作る
fusuma0325
0
2.1k
Kotlin/Nativeで作ってみるCLI, iOSアプリ
fusuma0325
1
140
Redashアラートの最近 - カスタマイズ機能を作った話
fusuma0325
0
850
Other Decks in Programming
See All in Programming
クライアントワークでSREをするということ。あるいは事業会社におけるSREと同じこと・違うこと
nnaka2992
1
320
Go1.26 go fixをプロダクトに適用して困ったこと
kurakura0916
0
350
nilとは何か 〜interfaceの構造とnil!=nilから理解する〜
kuro_kurorrr
3
1.9k
エンジニアの「手元の自動化」を加速するn8n 2026.02.27
symy2co
0
130
nuget-server - あなたが必要だったNuGetサーバー
kekyo
PRO
0
220
コーディングルールの鮮度を保ちたい / keep-fresh-go-internal-conventions
handlename
0
180
15年目のiOSアプリを1から作り直す技術
teakun
1
620
AI主導でFastAPIのWebサービスを作るときに 人間が構造化すべき境界線
okajun35
0
670
メタプログラミングで実現する「コードを仕様にする」仕組み/nikkei-tech-talk43
nikkei_engineer_recruiting
0
170
grapheme_strrev関数が採択されました(あと雑感)
youkidearitai
PRO
1
210
What Spring Developers Should Know About Jakarta EE
ivargrimstad
0
100
Codexに役割を持たせる 他のAIエージェントと組み合わせる実務Tips
o8n
3
1.2k
Featured
See All Featured
GraphQLとの向き合い方2022年版
quramy
50
14k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
5.4k
AI: The stuff that nobody shows you
jnunemaker
PRO
3
370
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
310
From π to Pie charts
rasagy
0
150
Crafting Experiences
bethany
1
81
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
55k
Making Projects Easy
brettharned
120
6.6k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
270
How Software Deployment tools have changed in the past 20 years
geshan
0
32k
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/