PostMessage per TWA

Sayed El-Abady
Sayed El-Abady

Da Chrome 115, la funzionalità Attività web attendibili può inviare messaggi utilizzando postMessage. Questo articolo illustra la configurazione necessaria per comunicare tra la tua app e il web.

Al termine della guida: - Comprendere il funzionamento della convalida di client e contenuti web. - Sapere come inizializzare il canale di comunicazione tra client e contenuti web. - Sapere come inviare e ricevere messaggi da contenuti web.

Per seguire questa guida ti serviranno:

  • Per aggiungere la libreria androidx.browser più recente (min v1.6.0-alpha02) al file build.gradle.
  • Chrome 115.0.5790.13 o versioni successive per TWA.

Il metodo window.postMessage() consente in modo sicuro la comunicazione multiorigine tra gli oggetti Window. Ad esempio, tra una pagina e un popup che ha generato o tra una pagina e un iframe incorporato al suo interno.

In genere, gli script su pagine diverse possono accedere l'uno all'altro solo se le pagine hanno la stessa origine, condividono lo stesso protocollo, lo stesso numero di porta e lo stesso host (noto anche come criterio della stessa origine). Il metodo window.postMessage() fornisce un meccanismo controllato per comunicare in modo sicuro tra origini diverse. Ciò può essere utile per implementare applicazioni di chat, strumenti di collaborazione e altro ancora. Ad esempio, un'applicazione di chat potrebbe utilizzare postMessage per inviare messaggi tra utenti che visitano siti web diversi. L'uso di postMessage in Attività web attendibili può essere un po' complicato; questa guida ti illustrerà come usare postMessage nel client TWA per inviare e ricevere messaggi dalla pagina web.

Aggiungi l'app alla convalida web

Affinché postMessage funzioni, è necessaria una relazione valida tra un sito web e l'app Attività web attendibile che sta lanciando questo sito. Puoi farlo con Digital Asset Links (DAL) aggiungendo il nome del pacchetto dell'app nel file assetlinks.json con la relazione use_as_origin, quindi sarà la seguente:

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

Tieni presente che la configurazione sull'origine associata al TWA deve fornire un'origine per il campo MessageEvent.origin, ma è possibile utilizzare postMessage per comunicare con altri siti che non includono il link agli asset digitali. Ad esempio, se sei il proprietario di www.example.com, dovrai dimostrarlo tramite DAL, ma puoi comunicare con qualsiasi altro sito web, ad esempio www.wikipedia.org.

Aggiungi PostMessageService al tuo manifest

Per ricevere comunicazioni relative a postMessage, devi configurare il servizio aggiungendo PostMessageService nel file manifest Android:

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

Recupera un'istanza CustomTabsSession

Dopo aver aggiunto il servizio al manifest, utilizza la classe CustomTabsClient per associare il servizio. Una volta stabilita la connessione, puoi utilizzare il client fornito per creare una nuova sessione, come indicato di seguito. CustomTabsSession è la classe di base per la gestione dell'API postMessage. Il codice seguente mostra come, una volta connesso il servizio, il client viene utilizzato per creare una nuova sessione. Questa sessione viene utilizzata per 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;
 }
});

Ti stai chiedendo cosa sia questa istanza di customTabsCallback, giusto. Lo creeremo nella prossima sezione.

Crea callback di schede personalizzate

CustomTabsCallback è una classe di callback che permette a CustomTabsClient di ricevere messaggi relativi agli eventi nelle sue schede personalizzate. Uno di questi eventi è onPostMessage e viene chiamato quando l'app riceve un messaggio dal web. Aggiungi il callback al client per inizializzare il canale postMessage e avviare la comunicazione, come mostrato nel codice seguente.

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.

 }
};

Comunicare dal web

Ora possiamo inviare e ricevere messaggi dall'app host. Come facciamo a fare lo stesso dal web? La comunicazione deve iniziare dall'app host, quindi la pagina web deve ottenere la porta dal primo messaggio. Questa porta viene utilizzata per comunicare con gli altri. Il file JavaScript sarà simile al seguente esempio:

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

Puoi trovare un esempio completo completo qui.

Foto di Joanna Kosinska su Unsplash