تنفيذ "إحصاءات Google لبرنامج Firebase" في Android Webview

1. مقدمة

تاريخ آخر تعديل: 2022-02-03

8cef5cc6581b73d0.png

ما ستتعلمه

  • كيفية إنشاء Webview بسيط جدًا في Android
  • كيفية إرسال أحداث Webview إلى Firebase

المتطلبات

  • مشروع Firebase تمّ تنفيذ حزمة تطوير البرامج (SDK) الخاصة بخدمة "إحصاءات Google" فيه
  • الإصدار 4.2 من "استوديو Android" أو إصدار أحدث
  • محاكي Android يعمل بالإصدار 5.0 من نظام التشغيل Android أو إصدار أحدث
  • الإلمام بلغة البرمجة Java
  • الإلمام بلغة البرمجة Javascript

2. إنشاء Webview بسيط على الويب في Android

إضافة 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>;

إضافة WebView في onCreate()

لتحميل صفحة ويب في 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 أساسي يعرض صفحة ويب.

استخدام JavaScript في Webviews

إذا كانت صفحة الويب التي تخطّط لتحميلها في WebView تستخدم JavaScript، عليك تفعيل JavaScript في WebView. بعد تفعيل JavaScript، يمكنك أيضًا إنشاء واجهات بين رمز تطبيقك ورمز JavaScript.

يتم إيقاف JavaScript في WebView تلقائيًا. يمكنك تفعيلها من خلال WebSettings المرفقة بـ WebView. يمكنك استرداد WebSettings باستخدام getSettings()‎، ثم تفعيل JavaScript باستخدام setJavaScriptEnabled()‎.

على سبيل المثال:

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 المربوطة

معالج JavaScript

تتمثّل الخطوة الأولى في استخدام "إحصاءات Google" في WebView في إنشاء دوال JavaScript لإعادة توجيه الأحداث وخصائص المستخدمين إلى الرمز البرمجي الأصلي. يوضّح المثال التالي كيفية إجراء ذلك بطريقة متوافقة مع الرمز البرمجي الأصلي لكلّ من Android وApple:

في هذا المثال، أنشأتُ ملف جافا سكريبت باسم script.js يتضمّن ما يلي :

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.");
  }
}

واجهة مدمجة مع المحتوى

لاستدعاء رمز Android أصلي من JavaScript، نفِّذ فئة تتضمّن طرقًا موسومة بـ @JavaScriptInterface: في المثال أدناه، أنشأتُ فئة Java جديدة باسم AnalyticsWebInterfcae.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");
});

ملف script.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");
});

هذه هي الطريقة الأساسية لإرسال الأحداث إلى "إحصاءات Google".

5- تصحيح أخطاء أحداث Webview في Firebase

تعمل عملية تصحيح أخطاء أحداث WebView في تطبيقك بالطريقة نفسها التي يتم بها تصحيح أخطاء أي جزء أصلي من حزمة SDK :

لتفعيل "وضع تصحيح الأخطاء"، يُرجى استخدام الأوامر التالية في وحدة تحكّم "استوديو Android":

adb shell setprop debug.firebase.analytics.app package_name

بعد الانتهاء، يمكنك اختبار الأحداث ومشاهدة أحداث Webview وهي تُسجَّل :

d230debf4ccfddad.png

6. تهانينا

تهانينا، لقد أنشأت WebView بنجاح في تطبيق Android. يمكنك إرسال وقياس أحداث المسار الرئيسية في تطبيقك التي تحدث من خلال WebViews. للاستفادة إلى أقصى حدّ من هذه الميزة، نقترح عليك أيضًا الربط بحساب "إعلانات Google" واستيراد هذه الأحداث كإحالات ناجحة.

لقد تعلّمت

  • كيفية إرسال أحداث Webview إلى Firebase
  • كيفية إعداد وإنشاء Webview بسيط في Android

المستندات المرجعية