PostMessage für TWA

Sayed El-Abady
Sayed El-Abady

Ab Chrome 115 können Sie über Vertrauenswürdige Webaktivitäten Nachrichten mit postMessage senden. In diesem Dokument wird beschrieben, wie Sie Ihre App und das Web einrichten.

Am Ende dieses Leitfadens werden Sie:

  • Erfahren Sie, wie die Client- und Webinhalt-Validierung funktioniert.
  • Sie müssen den Kommunikationskanal zwischen Client und Webcontent initialisieren.
  • Erfahren Sie, 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ürdige Webaktivitäten kann etwas kompliziert sein. In diesem Leitfaden erfährst du, wie du postMessage im TWA-Client verwendest, um Nachrichten an die Webseite zu senden und von ihr zu empfangen.

App zur Webvalidierung hinzufügen

Die postMessage API ermöglicht die Kommunikation zwischen zwei gültigen Ursprüngen, einer Quelle und einem Zielursprung. Damit die Android-App Nachrichten an den Zielursprung senden kann, muss sie deklarieren, welcher Quellursprung sie entspricht. Dazu können Sie mit Digital Asset Links (DAL) den Paketnamen der App in Ihre assetlinks.json-Datei mit der Beziehung als use_as_origin hinzufügen. Der Wert sieht dann 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";

// 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.
    }
};

Ü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