PostMessage لـ TWA

Sayed El-Abady
Sayed El-Abady

اعتبارًا من الإصدار 115 من Chrome، يمكن لأنشطة الويب الموثوق بها (TWA) إرسال الرسائل باستخدام postMessage. يستعرض هذا المستند عملية الإعداد اللازمة للتواصل بين تطبيقك والويب.

بنهاية هذا الدليل، ستتمكّن من:

  • فهم كيفية عمل التحقق من صحة محتوى الويب والعميل.
  • معرفة كيفية إعداد قناة التواصل بين العميل ومحتوى الويب
  • معرفة كيفية إرسال الرسائل إلى محتوى الويب واستلامها منه

اتّباع هذا الدليل يتطلّب ما يلي:

  • لإضافة أحدث إصدار من مكتبة androidx.browser (الإصدار الأدنى هو 1.6.0-alpha02) إلى ملف build.gradle.
  • الإصدار 115.0.5790.13 من Chrome أو إصدار أحدث من أجل TWA.

تتيح الطريقة window.postMessage() بأمان التواصل بين عناصر Window من مصادر مختلفة. على سبيل المثال، بين صفحة ونوافذ منبثقة تم إنشاؤها، أو بين صفحة وإطار iframe مضمّن فيها

عادةً، لا يُسمح عادةً للنصوص البرمجية الموجودة على صفحات مختلفة بالوصول إلى بعضها إلا إذا كانت الصفحات التي تنشأ من المصدر نفسه تتشارك نفس البروتوكول ورقم المنفذ والمضيف (تُعرَف أيضًا باسم سياسة المصدر نفسه). توفّر طريقة window.postMessage() آلية خاضعة للرقابة للتواصل بأمان بين مصادر مختلفة. ويمكن الاستفادة من ذلك في تنفيذ تطبيقات الدردشة والأدوات التعاونية وغيرها. على سبيل المثال، يمكن لتطبيق محادثة استخدام postMessage لإرسال رسائل بين المستخدمين على مواقع إلكترونية مختلفة. قد يكون استخدام postMessage في الأنشطة الموثوق بها على الويب (TWA) أمرًا صعبًا بعض الشيء، ويرشدك هذا الدليل إلى كيفية استخدام postMessage في برنامج TWA لإرسال الرسائل إلى صفحة الويب واستلامها منها.

إضافة التطبيق إلى عملية التحقّق من الويب

تسمح واجهة برمجة التطبيقات postMessage API لمصدرين صالحَين بالتواصل مع بعضهما، وهما مصدر ووجهة. لكي يتمكّن تطبيق Android من إرسال الرسائل إلى المصدر المستهدَف، يجب أن يُعلن عن مصدر المصدر الذي يعادله. يمكن إجراء ذلك باستخدام روابط مواد العرض الرقمية (DAL) من خلال إضافة اسم حزمة التطبيق في ملف assetlinks.json مع تحديد العلاقة على أنّها use_as_origin، وبذلك سيكون على النحو التالي:

[{
  "relation": ["delegate_permission/common.use_as_origin"],
  "target" : { "namespace": "android_app", "package_name": "com.example.app", "sha256_cert_fingerprints": [""] }
}]

يُرجى العلم أنّه عند الإعداد في المصدر المرتبط بـ TWA، يجب تقديم مصدر للحقل MessageEvent.origin، ولكن يمكن استخدام postMessage للتواصل مع مواقع إلكترونية أخرى لا تتضمّن رابط "مواد العرض الرقمية". على سبيل المثال، إذا كنت تملك www.example.com، عليك إثبات ذلك من خلال DAL، ولكن يمكنك التواصل مع أي مواقع إلكترونية أخرى، مثل www.wikipedia.org.

إضافة PostMessageService إلى البيان

لتلقّي إشعارات postMessage، عليك إعداد الخدمة، ويمكنك إجراء ذلك من خلال إضافة PostMessageService في ملف بيان Android:

<service android:name="androidx.browser.customtabs.PostMessageService"
android:exported="true"/>

الحصول على مثيل CustomTabsSession

بعد إضافة الخدمة إلى البيان، استخدِم الفئة CustomTabsClient لربط الخدمة. بعد الاتصال، يمكنك استخدام العميل المقدَّم لإنشاء جلسة جديدة على النحو التالي. CustomTabsSession هي الفئة الأساسية لمعالجة واجهة برمجة التطبيقات postMessage API. يوضِّح الرمز البرمجي التالي كيفية استخدام العميل لإنشاء جلسة جديدة بعد ربط الخدمة، ويتم استخدام هذه الجلسة postMessage:

private CustomTabsClient mClient;
private CustomTabsSession mSession;

// We use this helper method to return the preferred package to use for
// Custom Tabs.
String packageName = CustomTabsClient.getPackageName(this, null);

// Binding the service to (packageName).
CustomTabsClient.bindCustomTabsService(this, packageName, new CustomTabsServiceConnection() {
 @Override
 public void onCustomTabsServiceConnected(@NonNull ComponentName name,
     @NonNull CustomTabsClient client) {
   mClient = client;

   // Note: validateRelationship requires warmup to have been called.
   client.warmup(0L);

   mSession = mClient.newSession(customTabsCallback);
 }

 @Override
 public void onServiceDisconnected(ComponentName componentName) {
   mClient = null;
 }
});

أنت الآن تتساءل ما هو مثيل customTabsCallback هذا، أليس كذلك؟ سننشئ هذا في القسم التالي.

إنشاء CustomTabsCallback

CustomTabsCallback هي فئة ردّ اتصال لواجهة برمجة التطبيقات CustomTabsClient للحصول على رسائل بشأن الأحداث في علامات التبويب المخصّصة. أحد هذه الأحداث هو "onPostMessage"، ويتم الاتصال بهذا الحدث عندما يتلقّى التطبيق رسالة من الويب. أضِف دالة الاستدعاء إلى العميل لبدء قناة postMessage لبدء عملية التواصل، كما هو موضّح في الرمز البرمجي التالي.

private final String TAG = "TWA/CCT-PostMessageDemo";

// The origin the TWA is equivalent to, where the Digital Asset Links file
// was created with the "use_as_origin" relationship.
private Uri SOURCE_ORIGIN = Uri.parse("https://source-origin.example.com");

// The origin the TWA will communicate with. In most cases, SOURCE_ORIGIN and
// TARGET_ORIGIN will be the same.
private Uri TARGET_ORIGIN = Uri.parse("https://target-origin.example.com");

// It stores the validation result so you can check on it before requesting
// postMessage channel, since without successful validation it is not possible
// to use postMessage.
boolean mValidated;

CustomTabsCallback customTabsCallback = new CustomTabsCallback() {

    // Listens for the validation result, you can use this for any kind of
    // logging purposes.
    @Override
    public void onRelationshipValidationResult(int relation, @NonNull Uri requestedOrigin,
        boolean result, @Nullable Bundle extras) {
        // If this fails:
        // - Have you called warmup?
        // - Have you set up Digital Asset Links correctly?
        // - Double check what browser you're using.
        Log.d(TAG, "Relationship result: " + result);
        mValidated = result;
    }

    // Listens for any navigation happens, it waits until the navigation finishes
    // then requests post message channel using
    // CustomTabsSession#requestPostMessageChannel(sourceUri, targetUri, extrasBundle)

    // The targetOrigin in requestPostMessageChannel means that you can be certain their messages are delivered only to the website you expect.
    @Override
    public void onNavigationEvent(int navigationEvent, @Nullable Bundle extras) {
        if (navigationEvent != NAVIGATION_FINISHED) {
            return;
        }

        if (!mValidated) {
            Log.d(TAG, "Not starting PostMessage as validation didn't succeed.");
        }

        // If this fails:
        // - Have you included PostMessageService in your AndroidManifest.xml ?
        boolean result = mSession.requestPostMessageChannel(SOURCE_ORIGIN, TARGET_ORIGIN, new Bundle());
        Log.d(TAG, "Requested Post Message Channel: " + result);
    }

    // This gets called when the channel we requested is ready for sending/receiving messages.
    @Override
    public void onMessageChannelReady(@Nullable Bundle extras) {
        Log.d(TAG, "Message channel ready.");

        int result = mSession.postMessage("First message", null);
        Log.d(TAG, "postMessage returned: " + result);
    }

    // Listens for upcoming messages from Web.
    @Override
    public void onPostMessage(@NonNull String message, @Nullable Bundle extras) {
        super.onPostMessage(message, extras);
        // Handle the received message.
    }
};

الاتصال من الويب

يمكننا الآن إرسال الرسائل واستلامها من تطبيق المضيف، كيف يمكننا إجراء ذلك من الويب؟ يجب أن يبدأ الاتصال من التطبيق المضيف، ثم يجب أن تحصل صفحة الويب على المنفذ من الرسالة الأولى. يُستخدَم هذا المنفذ لإعادة الاتصال بالشبكة. سيظهر ملف JavaScript على النحو التالي:

window.addEventListener("message", function (event) {
  // We are receiveing messages from any origin, you can check of the origin by
  // using event.origin

  // get the port then use it for communication.
  var port = event.ports[0];
  if (typeof port === 'undefined') return;

  // Post message on this port.
  port.postMessage("Test")

  // Receive upcoming messages on this port.
  port.onmessage = function(event) {
    console.log("[PostMessage1] Got message" + event.data);
  };
});

يمكنك العثور على نموذج كامل هنا.

صورة Joanna Kosinska على Unsplash