Android WebView에서 Firebase용 Google 애널리틱스 구현

1. 소개

최종 업데이트: 2022년 2월 3일

8cef5cc6581b73d0.png

학습 내용

  • Android에서 매우 간단한 WebView를 만드는 방법
  • Firebase에 WebView 이벤트를 전송하는 방법

필요한 사항

  • 애널리틱스 SDK가 구현된 Firebase 프로젝트
  • Android 스튜디오 버전 4.2 이상
  • Android 5.0 이상을 실행하는 Android Emulator
  • Java 프로그래밍 언어에 대한 기본 지식
  • JavaScript 프로그래밍 언어에 대한 기본 지식

2. Android에서 간단한 웹 WebView 만들기

활동 레이아웃에 WebView 추가

레이아웃에서 앱에 WebView를 추가하려면 다음 코드를 활동의 레이아웃 XML 파일에 추가합니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".WebActivity"
>
  <WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
  />
</androidx.constraintlayout.widget.ConstraintLayout>;

onCreate()에서 WebView 추가

WebView에서 웹페이지를 로드하려면 loadUrl()을 사용하세요. WebView는 검은색 활동에서 만들어야 합니다. 예를 들어 onCreate 메서드에서 구현합니다.

public class WebActivity extends AppCompatActivity {
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web);
        // Navigate to site
        myWebView.loadUrl("https://bittererhu.glitch.me");
 }
}

그러나 이렇게 하려면 먼저 앱에서 인터넷에 액세스할 수 있어야 합니다. 인터넷 액세스 권한을 받으려면 매니페스트 파일에서 INTERNET 권한을 요청합니다. 예를 들면 다음과 같습니다.

<uses-permission android:name="android.permission.INTERNET" />

이것으로 웹페이지를 표시하는 기본 WebView에 필요한 것은 이것뿐입니다.

WebView에서 JavaScript 사용

WebView에 로드하려는 웹페이지가 JavaScript를 사용하는 경우 WebView에 JavaScript를 사용 설정해야 합니다. JavaScript가 사용 설정되면 앱 코드와 JavaScript 코드 간에 인터페이스를 만들 수도 있습니다.

JavaScript는 WebView에서 기본적으로 사용 중지되어 있습니다. WebView에 연결된 WebSettings를 통해 사용 설정할 수 있습니다. getSettings()로 WebSettings를 가져온 다음 setJavaScriptEnabled()로 JavaScript를 사용 설정할 수 있습니다.

예를 들면 다음과 같습니다.

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

업데이트된 활동 :

public class WebActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web);
        WebView myWebView = (WebView) findViewById(R.id.webview);
        if(myWebView != null) {
            WebSettings webSettings = myWebView.getSettings();
            webSettings.setJavaScriptEnabled(true);
        }
        // Navigate to site
        myWebView.loadUrl("https://bittererhu.glitch.me");
  }
}

be627fcc51a6179f.png

3. JavaScript 브리징된 인터페이스 구현

Javacript 핸들러

WebView에서 Google 애널리틱스를 사용하려면 먼저 이벤트 및 사용자 속성을 네이티브 코드로 전달하는 자바스크립트 함수를 만들어야 합니다. 다음 예에서는 Android 및 Apple 네이티브 코드 모두와 호환되는 방식으로 이 작업을 처리하는 방법을 보여줍니다.

이 예에서는 다음이 포함된 script.js라는 JavaScript 파일을 만들었습니다.

function logEvent(name, params) {
  if (!name) {
    return;
  }
  if (window.AnalyticsWebInterface) {
    // Call Android interface
    window.AnalyticsWebInterface.logEvent(name, JSON.stringify(params));
  } else if (window.webkit
      && window.webkit.messageHandlers
      && window.webkit.messageHandlers.firebase) {
    // Call iOS interface
    var message = {
      command: 'logEvent',
      name: name,
      parameters: params
    };
    window.webkit.messageHandlers.firebase.postMessage(message);
  } else {
    // No Android or iOS interface found
    console.log("No native APIs found.");
  }
}


function setUserProperty(name, value) {
  if (!name || !value) {
    return;
  }

  if (window.AnalyticsWebInterface) {
    // Call Android interface
    window.AnalyticsWebInterface.setUserProperty(name, value);
  } else if (window.webkit
      && window.webkit.messageHandlers
      && window.webkit.messageHandlers.firebase) {
    // Call iOS interface
    var message = {
      command: 'setUserProperty',
      name: name,
      value: value
   };
    window.webkit.messageHandlers.firebase.postMessage(message);
  } else {
    // No Android or iOS interface found
    console.log("No native APIs found.");
  }
}

네이티브 인터페이스

JavaScript에서 네이티브 Android 코드를 호출하려면 @JavaScriptInterface 메서드를 사용하여 클래스를 구현합니다. 아래 예에서는 AnalyticsWebInterfcae.java라는 새 Java 클래스를 만들었습니다.

public class AnalyticsWebInterface {

    public static final String TAG = "AnalyticsWebInterface";
    private FirebaseAnalytics mAnalytics;

    public AnalyticsWebInterface(Context context) {
        mAnalytics = FirebaseAnalytics.getInstance(context);
    }

    @JavascriptInterface
    public void logEvent(String name, String jsonParams) {
        LOGD("logEvent:" + name);
        mAnalytics.logEvent(name, bundleFromJson(jsonParams));
    }

    @JavascriptInterface
    public void setUserProperty(String name, String value) {
        LOGD("setUserProperty:" + name);
        mAnalytics.setUserProperty(name, value);
    }

    private void LOGD(String message) {
        // Only log on debug builds, for privacy
        if (BuildConfig.DEBUG) {
            Log.d(TAG, message);
        }
    }

    private Bundle bundleFromJson(String json) {
        // ...
    }
}

Once you have created the native interface, register it with your WebView so that it is visible to JavaScript code running in the WebView:

// Only add the JavaScriptInterface on API version JELLY_BEAN_MR1 and above, due to
// security concerns, see link below for more information:
// https://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object,%20java.lang.String)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
    mWebView.addJavascriptInterface(
            new AnalyticsWebInterface(this), AnalyticsWebInterface.TAG);
} else {
    Log.w(TAG, "Not adding JavaScriptInterface, API Version: " + Build.VERSION.SDK_INT);
}

최종 코드 :

// [START analytics_web_interface]
public class AnalyticsWebInterface {

    public static final String TAG = "AnalyticsWebInterface";
    private FirebaseAnalytics mAnalytics;

    public AnalyticsWebInterface(Context context) {
        mAnalytics = FirebaseAnalytics.getInstance(context);
    }
    @JavascriptInterface
    public void logEvent(String name, String jsonParams) {
        LOGD("logEvent:" + name);
        mAnalytics.logEvent(name, bundleFromJson(jsonParams));
    }
    @JavascriptInterface
    public void setUserProperty(String name, String value) {
        LOGD("setUserProperty:" + name);
        mAnalytics.setUserProperty(name, value);
    }
    private void LOGD(String message) {
        // Only log on debug builds, for privacy
        if (BuildConfig.DEBUG) {
            Log.d(TAG, message);
        }
    }
    private Bundle bundleFromJson(String json) {
        // [START_EXCLUDE]
        if (TextUtils.isEmpty(json)) {
            return new Bundle();
        }

        Bundle result = new Bundle();
        try {
            JSONObject jsonObject = new JSONObject(json);
            Iterator<String> keys = jsonObject.keys();

            while (keys.hasNext()) {
                String key = keys.next();
                Object value = jsonObject.get(key);
                if (value instanceof String) {
                    result.putString(key, (String) value);
                } else if (value instanceof Integer) {
                    result.putInt(key, (Integer) value);
                } else if (value instanceof Double) {
                    result.putDouble(key, (Double) value);
                } else {
                    Log.w(TAG, "Value for key " + key + " not one of [String, Integer, Double]");
                }
            }
        } catch (JSONException e) {
            Log.w(TAG, "Failed to parse JSON, returning empty Bundle.", e);
            return new Bundle();
        }
        return result;
        // [END_EXCLUDE]
    }

이제 JavaScript 인터페이스를 설정했으므로 분석 이벤트 전송을 시작할 수 있습니다.

4. 인터페이스를 통한 이벤트 전송

여기에서 볼 수 있듯이 WebView는 매우 간단합니다. 두 개 버튼으로 이벤트를 기록하고 다른 하나는 사용자 속성을 기록합니다.

7a00ed1192151b19.png

버튼을 클릭하면 'script.js' 파일이 호출되고 다음 코드가 실행됩니다.

document.getElementById("event1").addEventListener("click", function() {
    console.log("event1");
    logEvent("event1", { foo: "bar", baz: 123 });
});

document.getElementById("event2").addEventListener("click", function() {
  console.log("event2");
    logEvent("event2", { size: 123.456 });
});

document.getElementById("userprop").addEventListener("click", function() {
    console.log("userprop");
    setUserProperty("userprop", "custom_value");
});

최종 스크립트.js 파일 :

/* If you're feeling fancy you can add interactivity 
    to your site with Javascript */

// prints "hi" in the browser's dev tools console
console.log("hi");

// [START log_event]
function logEvent(name, params) {
  if (!name) {
    return;
  }

  if (window.AnalyticsWebInterface) {
    // Call Android interface
    window.AnalyticsWebInterface.logEvent(name, JSON.stringify(params));
  } else if (window.webkit
      && window.webkit.messageHandlers
      && window.webkit.messageHandlers.firebase) {
    // Call iOS interface
    var message = {
      command: 'logEvent',
      name: name,
      parameters: params
    };
    window.webkit.messageHandlers.firebase.postMessage(message);
  } else {
    // No Android or iOS interface found
    console.log("No native APIs found.");
  }
}
// [END log_event]

// [START set_user_property]
function setUserProperty(name, value) {
  if (!name || !value) {
    return;
  }

  if (window.AnalyticsWebInterface) {
    // Call Android interface
    window.AnalyticsWebInterface.setUserProperty(name, value);
  } else if (window.webkit
      && window.webkit.messageHandlers
      && window.webkit.messageHandlers.firebase) {
    // Call iOS interface
    var message = {
      command: 'setUserProperty',
      name: name,
      value: value
   };
    window.webkit.messageHandlers.firebase.postMessage(message);
  } else {
    // No Android or iOS interface found
    console.log("No native APIs found.");
  }
}
// [END set_user_property]

document.getElementById("event1").addEventListener("click", function() {
    console.log("event1");
    logEvent("event1", { foo: "bar", baz: 123 });
});

document.getElementById("event2").addEventListener("click", function() {
  console.log("event2");
    logEvent("event2", { size: 123.456 });
});

document.getElementById("userprop").addEventListener("click", function() {
    console.log("userprop");
    setUserProperty("userprop", "custom_value");
});

애널리틱스로 이벤트를 전송하는 기본 방법입니다.

5. Firebase에서 WebView 이벤트 디버그

앱에서 WebView 이벤트를 디버그하는 방법은 SDK의 네이티브 부분을 디버그하는 방법과 동일합니다.

디버그 모드를 사용 설정하려면 Android 스튜디오 콘솔에서 다음 명령어를 사용하세요.

adb shell setprop debug.firebase.analytics.app package_name

완료되면 WebView 이벤트가 실행되는지 테스트하고 확인할 수 있습니다.

d230debf4ccfddad.png

6. 축하합니다

축하합니다. Android 앱에서 WebView를 만들었습니다. 이제 WebView를 통해 발생하는 앱의 주요 유입경로 이벤트를 전송하고 측정할 수 있습니다. 이를 최대한 활용하려면 Google Ads에 연결하고 이러한 이벤트를 전환으로 가져오는 것이 좋습니다.

학습한 내용

  • Firebase에 WebView 이벤트를 전송하는 방법
  • Android에서 간단한 WebView를 설정하고 만드는 방법

참조 문서