PostMessage dla TWA

Sayed El-Abady
Sayed El-Abady

Od wersji Chrome 115 zaufane aplikacje internetowe (TWA) mogą wysyłać wiadomości za pomocą postMessage. Ten dokument przedstawia konfigurację niezbędnej do komunikacji między aplikacją a internetem.

Po zakończeniu tego przewodnika:

  • Dowiedz się, jak działa weryfikacja klienta i treści internetowej.
  • Naucz się zainicjować kanał komunikacji między klientem a treścią internetową.
  • Dowiedz się, jak wysyłać wiadomości do treści internetowych i je z nich odbierać.

Aby skorzystać z tego przewodnika, musisz mieć:

  • Aby dodać najnowszą bibliotekę androidx.browser (min.wersja 1.6.0-alfa02) do pliku build.gradle,:
  • Chrome w wersji 115.0.5790.13 lub nowszej na potrzeby TWA.

Metoda window.postMessage() bezpiecznie umożliwia komunikację między domenami w klasie Window. np. między stroną a otwartym wyskakującym okienkiem lub między stroną a umieszczonym w niej elementem iframe.

Zwykle skrypty na różnych stronach mogą uzyskiwać dostęp do siebie tylko wtedy, gdy strony, które pochodzą z tego samego źródła, mają ten sam protokół, numer portu i hosta (tzw. zasada tego samego pochodzenia). Metoda window.postMessage() zapewnia kontrolowany mechanizm bezpiecznej komunikacji między różnymi źródłami. Może to być przydatne podczas implementacji aplikacji do czatu, narzędzi do współpracy i innych. Aplikacja do obsługi czatu może na przykład używać polecenia postMessage do wysyłania wiadomości między użytkownikami znajdującymi się w różnych witrynach. Korzystanie z postMessage w programie TWA może być nieco skomplikowane. W tym przewodniku pokażemy, jak używać postMessage w kliencie TWA do wysyłania wiadomości na stronę internetową i odbierania ich z niej.

Dodawanie aplikacji do weryfikacji internetowej

Interfejs postMessage API pozwala na komunikowanie się ze sobą 2 prawidłowych źródeł: źródłowego i docelowego. Aby aplikacja na Androida mogła wysyłać wiadomości do źródła docelowego, musi zadeklarować źródło źródłowe, któremu jest równoważne. Aby to zrobić, użyj Digital Asset Links (DAL), dodając nazwę pakietu aplikacji w pliku assetlinks.json za pomocą relacji use_as_origin w taki sposób:

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

Pamiętaj, że podczas konfiguracji źródła powiązanego z TWA musisz podać źródło w polu MessageEvent.origin, ale za pomocą postMessage możesz komunikować się z innymi stronami, które nie zawierają Digital Assets Link. Jeśli na przykład witryna www.example.com należy do Ciebie, musisz to udowodnić za pomocą DAL, ale możesz komunikować się z innymi stronami, na przykład www.wikipedia.org.

Dodaj PostMessageService do pliku manifestu

Aby otrzymywać wiadomości na temat usługi postMessage, musisz ją skonfigurować, dodając PostMessageService do pliku manifestu Androida:

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

Pobieranie instancji CustomTabsSession

Po dodaniu usługi do pliku manifestu użyj klasy CustomTabsClient, aby ją powiązać. Po nawiązaniu połączenia możesz użyć podanego klienta do utworzenia nowej sesji w następujący sposób. Główna klasa obsługi interfejsu postMessage to CustomTabsSession. Poniższy kod pokazuje, w jaki sposób po połączeniu usługi klient jest używany do utworzenia nowej sesji, która służy do 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;
 }
});

Zastanawiasz się, co to za wystąpienie customTabsCallback? Wprowadzimy je w następnej sekcji.

Utwórz wywołanie zwrotne kart niestandardowych

CustomTabsCallback to klasa wywołania zwrotnego dla CustomTabsClient, która pobiera wiadomości dotyczące zdarzeń na kartach niestandardowych. Jedno z tych zdarzeń to onPostMessage, które jest wywoływane, gdy aplikacja otrzyma wiadomość z internetu. Dodaj wywołanie zwrotne do klienta, aby zainicjować kanał postMessage i rozpocząć komunikację, jak pokazano w poniższym kodzie.

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

Komunikowanie się z internetu

Teraz możemy wysyłać i odbierać wiadomości z naszej aplikacji hostującej. Jak możemy robić to w sieci? Komunikacja musi rozpoczynać się od aplikacji hosta, a następnie strona internetowa musi pobrać port z pierwszej wiadomości. Ten port służy do komunikacji zwrotnej. Plik JavaScript będzie wyglądać mniej więcej tak:

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

Pełną próbkę treści znajdziesz tutaj

Zdjęcie: Joanna Kosinska, Unsplash