AndroidアプリのWebView内から発生させたイベントをネイティブ側のFirebase向けGoogleアナリティクス(あるいはApp+Webプロパティ、GA4)にて計測するための方法。
Googleの公式ヘルプガイドにandroid向けにはJavaのサンプルコードが記載されていたもののKoltlin版が無かったので作成しました。
1. Android プロジェクトに Firebase SDKを導入
以下リンクの「オプション1」の手順通りにやればらくらく。
https://firebase.google.com/docs/android/setup
(iOSアプリの場合はこちら : https://firebase.google.com/docs/ios/setup )
最後の手順の「アプリを実行してインストールを確認」が何度リトライしても上手く行きませんが大抵スキップして翌日チェックしたら成功してます。
2. FirebaseプロジェクトをGoogleアナリティクスに接続
以下の手順でFirebaseプロジェクトをGoogleアナリティクスに接続します。
3. Webサイト側にJavaScript ハンドラを実装
以下Googleヘルプガイド記載のJavaScriptコードをWebViewで開かれる可能性があるすべてのWebページに実装することで、 logEvent() とsetUserProperty()をJavaScriptから使えるようになります。
JavaScript コード
Googleヘルプガイドのスクリプトをそのまま加工せずにご利用ください。
Android・iOS共通。アプリ側でネイティブインターフェースが実装されていない場合はconsole.log("No native APIs found.");
が実行されるため、User Agentによるアプリ判定も特別な理由がない限り実装不要です。
→ https://firebase.google.com/docs/analytics/webview?platform=android#implement-javascript-handler
サイトへのlogEvent() 実装例
FirebaseアナリティクスのJavaScriptハンドラを実装・更にアプリ側に後述のネイティブインターフェースを実装した後、あるボタンのタップ数をFirebaseで計測したい場合、以下のようなコードでWebView内で発生したイベントをGA4に送信できるようになります。
<button type="button" onclick="logEvent('tap_button',{'param_foo':'key_bar','param_baz':'key_qux'})">logEvent Test Button</button>
取得したいイベントがGA4で推奨イベントとして用意されているものであれば可能なかぎり推奨イベント名とGoogle指定のパラメータに従います。
独自のイベント名、パラメータ名を設定しても問題ありませんが、イベント名は40文字以下の半角英数字・アンダースコアで構成され、アルファベットから始まり、パラメータの値は100文字以内である必要があります。
また、推奨イベント・推奨パラメータがadd_payment_info
, item_id
のように命名されているため、独自のイベントなどを定義する場合もスネークケース(小文字の英語をアンダースコアで繋ぐ記述)を使った方が整合性が保てるのでオススメです。
その他の制限事項 → Sending events | Measurement Protocol for Google Analytics 4
4. アプリ側にネイティブ インターフェースを実装
ヘルプガイドにKotlin版が無かったため、Javaのコードを変換して作成しています。
AnalyticsWebInterfaceクラスを実装
Kotlin コード
/** Instantiate the interface and set the context */
class AnalyticsWebInterface (context: Context) {
companion object{
const val TAG = "AnalyticsWebInterface"
}
private val firebaseAnalytics: FirebaseAnalytics = FirebaseAnalytics.getInstance(context)
@JavascriptInterface
fun logEvent(name: String, jsonParams: String) {
LOGD("logEvent:$name")
firebaseAnalytics.logEvent(name, bundleFromJson(jsonParams))
}
@JavascriptInterface
fun setUserProperty(name: String, value: String?) {
LOGD("setUserProperty:$name")
firebaseAnalytics.setUserProperty(name, value)
}
private fun LOGD(message: String) {
// Only log on debug builds, for privacy
if (BuildConfig.DEBUG) {
Log.d(TAG, message)
}
}
private fun bundleFromJson(json: String): Bundle? {
if (TextUtils.isEmpty(json)) {
return Bundle()
}
val result = Bundle()
try {
val jsonObject = JSONObject(json)
val keys = jsonObject.keys()
while (keys.hasNext()) {
val key = keys.next()
val value = jsonObject[key]
when(value){
is String -> result.putString(key, value)
is Int -> result.putInt(key, value)
is Double -> result.putDouble(key, value)
else -> Log.w(TAG, "Value for key $key not one of [String, Integer, Double]")
}
}
} catch (e: JSONException) {
Log.w(TAG, "Failed to parse JSON, returning empty Bundle.", e)
return Bundle()
}
return result
}
}
必要なimport文は赤字部分にマウスオーバーして Alt+Enter で適宜追加
Javaの場合 https://firebase.google.com/docs/analytics/webview?platform=android&hl=en#implement_native_interface
(iOSアプリの場合はこちら : https://firebase.google.com/docs/analytics/webview?platform=ios#implement_native_interface )
eCommerceイベントを計測したいとき
現在公式ヘルプにサンプルコードとして記載されているネイティブインターフェースのコードはitemsパラメータに対応していない。もしeCommerceイベントを計測したいときはbundleFromJson()
関数を以下のように変更する。
private fun bundleFromJson(json: String): Bundle? {
if (TextUtils.isEmpty(json)) {
return Bundle()
}
val result = Bundle()
try {
val jsonObject = JSONObject(json)
val keys = jsonObject.keys()
while (keys.hasNext()) {
val key = keys.next()
val value = jsonObject[key]
when(value){
is String -> result.putString(key, value)
is Int -> result.putInt(key, value)
is Double -> result.putDouble(key, value)
is JSONArray -> if(Regex("^items$").matches(key)){
val items = ArrayList<Bundle>()
for(i in 0 until value.length()){
val itemObj = value.getJSONObject(i)
val itemKeys = itemObj.keys()
val item = Bundle()
while(itemKeys.hasNext()){
val itemKey = itemKeys.next()
val itemValue = itemObj[itemKey]
when(itemValue) {
is String -> item.putString(itemKey, itemValue)
is Int -> item.putInt(itemKey, itemValue)
is Double -> item.putDouble(itemKey, itemValue)
else -> Log.w(TAG, "Value for item key $itemKey not one of [String, Integer, Double]")
}
}
items.add(item)
}
result.putParcelableArrayList(key,items)
}else{
Log.w(TAG, "Value for key $key not one of [JSONArray(item)]")
}
else -> Log.w(TAG, "Value for key $key not one of [String, Integer, Double, JSONArray(item)]")
}
}
} catch (e: JSONException) {
Log.w(TAG, "Failed to parse JSON, returning empty Bundle.", e)
return Bundle()
}
return result
}
ネイティブインターフェースをWebViewに紐づけ
先ほど作成したネイティブインターフェースをWebViewリソースに紐づけ
Kotlin コード
コード内の「 webview
」の文字を目的のリソースIDに変更の上でご利用ください。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
webview.addJavascriptInterface(AnalyticsWebInterface(this), AnalyticsWebInterface.TAG);
} else {
Log.w(AnalyticsWebInterface.TAG, "Not adding JavaScriptInterface, API Version: " + Build.VERSION.SDK_INT);
}
(オプション)テストアプリのWebViewのデバッグを許可
もしテストアプリのWebView内での通信内容やJavaScriptの詳細な動作などの情報をPC側のChrome Deveroper Toolで確認したい場合、以下の記述も追加が必要です。
※ セキュリティ上の問題を生む場合があるため本番アプリでこの記述は行わないで下さい。
Kotlin コード
コード内の「 webview
」の文字を目的のリソースIDに変更の上でご利用ください。
webView.setWebContentsDebuggingEnabled(true);
DebugViewによるAndroidアプリのイベント計測状況の確認
手順
- PC にadbコマンドを導入する → ADBコマンド導入の方法 - Qiita
- 検証用の Android 端末にデバッグ対象のアプリをインストール
- Android 端末のデバッグモードを有効化
- Android 端末をPCにUSBデバッグモードで接続
- PC でコマンドプロンプトを起動し、コマンド
adb devices
を実行。以下のように端末名とdevice
の文字が表示されれば次の手順へ。unauthorize
と出た場合は端末側でUSBデバッグの認証が通っていないので端末を操作して許可させる。
- コマンド
adb shell setprop debug.firebase.analytics.app ●●●
を実行(黒丸部分はデバッグ対象のパッケージ名に置換えた上で実行) - Android 端末を PC から外してアプリを操作する
- PC のブラウザで Firebase Console を開き、目的の Firebase プロジェクト画面の左側ナビゲーション内の 分析 > DebugView を選択して開く
- 「デバッグに使用するデバイス」に先ほど自分が設定した Android 端末があることを確認して選択
- Android 端末で操作した内容が反映されるか確認する(15秒程度の遅延あり)
イベントが計測されている様子
以下のようにイベントが計測されれば実装および検証が成功。お疲れ様でした。
表示されたイベントをクリックしてパラメータも正常に計測できていることを確認
以上。
#参考
WebView でアナリティクスを使用する | Firebase
- Android(Java) : https://developers.google.com/analytics/devguides/collection/firebase/android/webview
- iOS(Swift, Objective-C) : https://firebase.google.com/docs/analytics/webview?platform=iOS