PostMessage für TWA

Sayed El-Abady
Sayed El-Abady

Ab Chrome 115 können Sie über Vertrauenswürdige Webaktivitäten Nachrichten mit postMessages senden. In diesem Artikel erfahren Sie, wie Sie die Kommunikation zwischen Ihrer App und dem Web einrichten.

In diesem Leitfaden erfahren Sie, wie die Client- und Webinhaltsvalidierung funktioniert. - Den Kommunikationskanal zwischen Client und Webinhalten initialisieren – Sie wissen, wie Sie Nachrichten an Webinhalte senden und von diesen empfangen können.

Um dieser Anleitung zu folgen, benötigen Sie Folgendes:

  • Sie fügen der Datei „build.gradle“ die neueste Bibliothek androidx.browser (mind. v1.6.0-alpha02) hinzu.
  • Chrome-Version 115.0.5790.13 oder höher für TWA.

Die Methode window.postMessage() ermöglicht auf sichere Weise die ursprungsübergreifende Kommunikation zwischen Window-Objekten. Zum Beispiel zwischen einer Seite und einem Pop-up, das sie erstellt hat, oder zwischen einer Seite und einem darin eingebetteten iFrame.

In der Regel dürfen Skripts auf verschiedenen Seiten nur aufeinander zugreifen, wenn die Seiten aus demselben Ursprung stammen und dasselbe Protokoll, dieselbe Portnummer und denselben Host verwendet werden. Dies wird auch als Richtlinie für denselben Ursprung bezeichnet. Die Methode window.postMessage() bietet einen kontrollierten Mechanismus für die sichere Kommunikation zwischen verschiedenen Ursprüngen. Dies kann bei der Implementierung von Chat-Anwendungen, Tools für die Zusammenarbeit und mehr nützlich sein. Eine Chatanwendung könnte beispielsweise über postMessage Nachrichten zwischen Nutzern senden, die sich auf verschiedenen Websites befinden. Die Verwendung von postMessage in vertrauenswürdigen Web-Aktivitäten (TWA) kann etwas schwierig sein. In diesem Leitfaden wird beschrieben, wie du postMessage im TWA-Client verwendest, um Nachrichten an die Webseite zu senden und von ihr zu empfangen.

App zur Webvalidierung hinzufügen

Damit die postMessage funktioniert, ist eine gültige Beziehung zwischen einer Website und der App für vertrauenswürdige Webaktivitäten erforderlich, die diese Website startet. Dazu können Sie Digital Asset Links (DAL) verwenden. Fügen Sie dazu den Paketnamen der App in Ihre assetlinks.json-Datei mit der Beziehung zu use_as_origin ein. Dies sieht so aus:

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

Bei der Einrichtung auf der mit der TWA verknüpften Quelle muss eine Quelle für das Feld MessageEvent.origin angegeben werden. postMessage kann jedoch für die Kommunikation mit anderen Websites verwendet werden, die den Link für digitale Assets nicht enthalten. Wenn du beispielsweise Inhaber von www.example.com bist, musst du das über DAL nachweisen. Du kannst aber auch mit allen anderen Websites kommunizieren, z. B. www.wikipedia.org.

PostMessageService zum Manifest hinzufügen

Um postMessage-Benachrichtigungen zu erhalten, musst du den Dienst einrichten, indem du PostMessageService in dein Android-Manifest einfügst:

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

CustomTabsSession-Instanz abrufen

Nachdem Sie den Dienst zum Manifest hinzugefügt haben, verwenden Sie die Klasse CustomTabsClient, um den Dienst zu binden. Sobald die Verbindung hergestellt ist, können Sie den bereitgestellten Client zum Erstellen einer neuen Sitzung wie folgt verwenden. CustomTabsSession ist die Kernklasse für die Verarbeitung der postMessage API. Der folgende Code zeigt, wie der Client, sobald die Verbindung zum Dienst hergestellt ist, zum Erstellen einer neuen Sitzung verwendet wird. Diese Sitzung wird für postMessage verwendet:

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

Sie fragen sich jetzt, was diese customTabsCallback-Instanz ist, richtig? Diese erstellen wir im nächsten Abschnitt.

CustomTabsCallback erstellen

CustomTabsCallback ist eine Callback-Klasse für CustomTabsClient, mit der Nachrichten zu Ereignissen auf benutzerdefinierten Tabs abgerufen werden. Eines dieser Ereignisse ist onPostMessage und wird aufgerufen, wenn die App eine Nachricht aus dem Web empfängt. Fügen Sie den Callback zum Client hinzu, um den postMessage-Kanal zu initialisieren und die Kommunikation zu starten, wie im folgenden Code gezeigt.

private final String TAG = "TWA/CCT-PostMessageDemo";
private Uri SOURCE_ORIGIN = Uri.parse("my-app-origin-uri");
private Uri TARGET_ORIGIN = Uri.parse("website-you-are-communicating-with");

// It stores the validation result so you can check on it before requesting postMessage channel, since without successful validation it is not posible 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.

 }
};

Über das Web kommunizieren

Jetzt können wir über unsere Host-App Nachrichten senden und empfangen. Wie können wir dasselbe auch über das Web machen? Die Kommunikation muss von der Host-App aus beginnen. Anschließend muss die Webseite den Port aus der ersten Nachricht abrufen. Dieser Port wird für die Kommunikation verwendet. Ihre JavaScript-Datei sieht ungefähr so aus:

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

Ein vollständiges Beispiel finden Sie hier.

Foto von Joanna Kosinska auf Unsplash