No Chrome 115, as Atividades Confiáveis na Web (TWA, na sigla em inglês) do Chrome 115 podem enviar mensagens usando postMessage. Este documento explica a configuração necessária para a comunicação entre o app e a Web.
Ao final deste guia, você:
- Entenda como funciona a validação do conteúdo da Web e do cliente.
- saber como inicializar o canal de comunicação entre o cliente e o conteúdo da Web;
- saber enviar e receber mensagens de conteúdo da Web;
Para seguir este guia, você precisará de:
- Para adicionar a biblioteca androidx.browser (min v1.6.0-alpha02) mais recente ao arquivo build.gradle,
- Chrome versão 115.0.5790.13 ou mais recente para TWA.
O método window.postMessage()
permite a comunicação de origem cruzada entre objetos Window com segurança. Por exemplo, entre uma página e um pop-up que ela gerou, ou entre uma página e um iframe incorporado a ela.
Normalmente, os scripts de páginas diferentes só podem acessar uns aos outros se as páginas tiverem a mesma origem e compartilharem o mesmo protocolo, número de porta e host (também conhecido como política de mesma origem). O método window.postMessage()
oferece um mecanismo controlado para comunicação segura entre diferentes origens. Isso pode ser útil para a implementação de aplicativos de chat, ferramentas colaborativas e outros. Por exemplo, um aplicativo de chat pode usar postMessage
para enviar mensagens entre usuários que estão em sites diferentes.
Usar postMessage
em Atividades confiáveis na Web (TWA, na sigla em inglês) pode ser um pouco complicado. Este guia explica como usar o postMessage no cliente TWA para enviar e receber mensagens da página da Web.
Adicionar o app à validação da Web
A API postMessage permite que duas origens válidas se comuniquem umas com as outras: uma origem e uma origem de destino. Para que o app Android possa enviar mensagens à origem de destino, ele precisa declarar a qual origem ela é equivalente. Isso pode ser feito com os Digital Asset Links (DAL, na sigla em inglês) adicionando o nome do pacote do app ao arquivo assetlinks.json
com a relação como use_as_origin
para que fique assim:
[{
"relation": ["delegate_permission/common.use_as_origin"],
"target" : { "namespace": "android_app", "package_name": "com.example.app", "sha256_cert_fingerprints": [""] }
}]
Observe que, ao configurar a origem associada ao TWA, é necessário fornecer uma origem para o campo MessageEvent.origin, mas postMessage
pode ser usado para se comunicar com outros sites que não incluam o Digital Assets Link. Por exemplo, se você for o proprietário de www.example.com
, vai precisar provar isso usando a DAL, mas poderá se comunicar com qualquer outro site, como www.wikipedia.org
.
Adicionar o PostMessageService ao seu manifesto
Para receber comunicações de postMessage
, configure o serviço adicionando o PostMessageService
ao manifesto do Android:
<service android:name="androidx.browser.customtabs.PostMessageService"
android:exported="true"/>
Acessar uma instância CustomTabsSession
Depois de adicionar o serviço ao manifesto, use a classe CustomTabsClient para vincular o serviço. Depois de se conectar, você pode usar o cliente fornecido para criar uma nova sessão, conforme mostrado a seguir.
CustomTabsSession é a classe principal para lidar com a API postMessage. O código a seguir mostra como, depois que o serviço é conectado, o cliente é usado para criar uma nova sessão e essa sessão é usada 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;
}
});
Você está se perguntando o que é essa instância de customTabsCallback
, certo? Vamos criar isso na próxima seção.
Criar CustomTabsCallback
CustomTabsCallback é uma classe de callback para o CustomTabsClient receber mensagens sobre eventos nas guias personalizadas. Um desses eventos é o onPostMessage
, que é chamado quando o app recebe uma mensagem da Web. Adicione o callback ao cliente para inicializar o canal postMessage para iniciar a comunicação, conforme mostrado no código a seguir.
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.
}
};
Como se comunicar pela Web
Agora que podemos enviar e receber mensagens do nosso aplicativo host, como podemos fazer o mesmo na Web? A comunicação tem que começar no app host. Em seguida, a página da Web precisará conseguir a porta na primeira mensagem. Essa porta é usada para comunicação de retorno. O arquivo JavaScript será parecido com este exemplo:
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);
};
});
Confira um exemplo completo aqui.
Foto de Joanna Kosinska no Unsplash (links em inglês)