Desde Chrome 115, las Actividades web de confianza (TWA) pueden enviar mensajes con postMessage. En este documento, se explica la configuración necesaria para establecer la comunicación entre tu app y la Web.
Al final de esta guía, harás lo siguiente:
- Comprende cómo funciona la validación de cliente y de contenido web.
- Saber cómo inicializar el canal de comunicación entre el cliente y el contenido web
- Conoce cómo enviar y recibir mensajes de contenido web.
Para seguir esta guía, necesitarás lo siguiente:
- Para agregar la biblioteca androidx.browser más reciente (v1.6.0-alpha02 como mínimo) a tu archivo build.gradle,
- Chrome versión 115.0.5790.13 o posterior para TWA.
El método window.postMessage()
habilita de forma segura la comunicación de origen cruzado entre objetos Window. Por ejemplo, entre una página y una ventana emergente que generó, o entre una página y un iframe incorporado en ella.
Por lo general, las secuencias de comandos de diferentes páginas solo pueden acceder unas a otras si las páginas donde se originan en el mismo origen comparten el mismo protocolo, número de puerto y host (también conocida como política del mismo origen). El método window.postMessage()
proporciona un mecanismo controlado para comunicarse de forma segura entre diferentes orígenes. Esto puede ser útil para implementar aplicaciones de chat, herramientas de colaboración y mucho más. Por ejemplo, una aplicación de chat podría usar postMessage
para enviar mensajes entre usuarios que estén en sitios web diferentes.
El uso de postMessage
en Trusted Web Activities (TWA) puede ser un poco complicado. En esta guía, se explica cómo usar postMessage en el cliente TWA para enviar y recibir mensajes de la página web.
Agrega la app a la validación web
La API de postMessage permite que dos orígenes válidos se comuniquen entre sí: un origen y un origen objetivo. Para que la aplicación para Android pueda enviar mensajes al origen de destino, debe declarar a qué origen de origen equivale. Para ello, puedes usar Vínculos de recursos digitales (DAL). Para ello, agrega el nombre del paquete de la app al archivo assetlinks.json
con una relación como use_as_origin
para que quede de la siguiente manera:
[{
"relation": ["delegate_permission/common.use_as_origin"],
"target" : { "namespace": "android_app", "package_name": "com.example.app", "sha256_cert_fingerprints": [""] }
}]
Ten en cuenta que la configuración en el origen asociado con la TWA. Debes proporcionar un origen para el campo MessageEvent.origin, pero se puede usar postMessage
para comunicarte con otros sitios que no incluyen el Vínculo de recursos digitales. Por ejemplo, si eres el propietario de www.example.com
, deberás probarlo a través de DAL, pero podrás comunicarte con cualquier otro sitio web (por ejemplo, www.wikipedia.org
).
Cómo agregar el PostMessageService a tu manifiesto
Para recibir comunicación con postMessage
, debes configurar el servicio agregando PostMessageService
en tu manifiesto de Android:
<service android:name="androidx.browser.customtabs.PostMessageService"
android:exported="true"/>
Cómo obtener una instancia CustomTabsSession
Después de agregar el servicio al manifiesto, usa la clase CustomTabsClient para vincular el servicio. Una vez que te conectes, puedes usar el cliente proporcionado para crear una sesión nueva de la siguiente manera.
CustomTabsSession es la clase principal para controlar la API de postMessage. En el siguiente código, se muestra cómo una vez que se conecta el servicio, se usa el cliente a fin de crear una sesión nueva. Esta sesión se usa para 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;
}
});
Te preguntas qué es esta instancia de customTabsCallback
. Crearemos esto en la próxima sección.
Crear CustomTabsCallback
CustomTabsCallback es una clase de devolución de llamada para CustomTabsClient a fin de recibir mensajes sobre eventos en sus pestañas personalizadas. Uno de estos eventos es onPostMessage
, y se llama a él cuando la app recibe un mensaje de la Web. Agrega la devolución de llamada al cliente para inicializar el canal postMessage y comenzar la comunicación, como se muestra en el siguiente código.
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.
}
};
Cómo comunicarte desde la Web
Ahora podemos enviar y recibir mensajes desde nuestra app host, ¿cómo hacemos lo mismo desde la Web? La comunicación debe comenzar desde la app host y, luego, la página web debe obtener el puerto del primer mensaje. Este puerto se usa para establecer la comunicación. El archivo JavaScript tendrá un aspecto similar al siguiente ejemplo:
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);
};
});
Puedes encontrar una muestra completa aquí.
Foto de Joanna Kosinska en Unsplash