Esta página discute os vários métodos e práticas recomendadas para estabelecer uma ponte nativa, também conhecida como ponte JavaScript, para facilitar a comunicação entre o conteúdo da Web em uma WebView e um app Android host.
Isso permite que desenvolvedores da Web usem JavaScript para acessar recursos nativos da plataforma, como câmera, sistema de arquivos ou sensores de hardware avançados, que as APIs da Web padrão normalmente não oferecem.
Casos de uso
Uma implementação de ponte JavaScript permite vários cenários de integração em que o conteúdo da Web exige acesso mais profundo ao sistema operacional Android. Confira alguns exemplos:
- Integração de plataforma: acionamento de componentes nativos da interface do Android (por exemplo, solicitações biométricas,
BottomSheetDialog) em uma página da Web. - Performance: descarregamento de tarefas computacionais pesadas para código nativo Java ou Kotlin.
- Persistência de dados: acesso a bancos de dados criptografados locais ou preferências compartilhadas.
- Transferências de dados grandes: passagem de arquivos de mídia ou estruturas de dados complexas entre o app e o renderizador da Web.
Mecanismos de comunicação
O Android oferece três gerações principais de APIs para estabelecer uma ponte nativa. Embora todos ainda estejam disponíveis, eles diferem significativamente em segurança, usabilidade e desempenho.
Usar addWebMessageListener (recomendado)
addWebMessageListener é a abordagem mais moderna e recomendada para
comunicação entre o conteúdo da Web e o código do app nativo. Ele combina a facilidade
de uso da interface JavaScript com a segurança do sistema de mensagens.
Como funciona: o app adiciona um listener com um nome específico e um conjunto de regras de origem permitidas. O WebView garante que o objeto JavaScript esteja
presente no escopo global (window.objectName) desde o momento em que a página
começa a ser carregada.
Inicialização: para garantir que o WebView injete o objeto JavaScript antes da
execução de qualquer script, chame addWebMessageListener antes de navegar até a
página (como chamar WebViewCompat.navigate ou loadUrl).
Principais recursos:
Segurança e confiança: ao contrário das APIs legadas, esse método exige um
Set<String>deallowedOriginRulesdurante a inicialização. Esse é o mecanismo principal para estabelecer confiança.Quando você especifica uma origem confiável, como
https://example.com, o WebView garante que só expõe os objetos JavaScript injetados para páginas da Web carregadas dessa origem exata.O callback do listener nativo recebe um parâmetro
sourceOrigincom cada mensagem. Isso pode ser usado para verificar a origem exata do remetente se sua ponte aceitar várias origens permitidas.Como a WebView aplica estritamente essas verificações de origem no nível da plataforma, seu app geralmente pode confiar nas mensagens recebidas de um
sourceOriginconfiável como verdadeiras, eliminando a necessidade de validação rigorosa de payload na maioria das implementações padrão.- A WebView compara as regras com o esquema (HTTP/HTTPS), o host e a porta.
- O WebView ignora caminhos. Por exemplo,
https://example.compermitehttps://example.com/loginehttps://example.com/home. - A WebView limita estritamente os caracteres curinga ao início do host para
subdomínios. Por exemplo,
https://*.example.comcorresponde ahttps://foo.example.com, mas não ahttps://example.com. Se você precisar corresponder ahttps://example.come aos subdomínios dele, adicione cada regra de origem separadamente à lista de permissões (por exemplo,"https://example.com", "https://*.example.com"). Não é possível usar caracteres curinga no esquema ou no meio de um domínio.
Isso restringe a ponte a domínios verificados, impedindo que conteúdo de terceiros não autorizado ou iframes injetados executem código nativo.
Suporte a vários frames: funciona em todos os frames que correspondem às regras de origem.
Linhas de execução: o callback do listener é executado na linha de execução principal (de interface) do aplicativo. Se a ponte precisar processar dados complexos, analisar JSON ou fazer pesquisas no banco de dados, descarregue esse trabalho para uma linha de execução em segundo plano para evitar o congelamento da interface do usuário do aplicativo com um erro "O app não está respondendo" (ANR).
Bidirecional: quando a página da Web envia uma mensagem, o app recebe um
JavaScriptReplyProxyque pode ser usado para enviar mensagens de volta a esse frame específico. Você pode reter esse objetoreplyProxye usá-lo a qualquer momento para enviar qualquer número de mensagens à página, não apenas para responder a cada mensagem individual enviada por ela. Se o frame de origem sair da página ou for destruído, as mensagens enviadas usandopostMessage()no proxy serão ignoradas silenciosamente.Início no lado do app: embora a página da Web sempre precise iniciar o canal de comunicação com o app, o app nativo pode solicitar unilateralmente que a página da Web comece esse processo. O app nativo pode se comunicar com a página da Web usando
addDocumentStartJavaScript()(para avaliar o JavaScript antes do carregamento da página) ouevaluateJavaScript()(para avaliar o JavaScript depois que a página for carregada).
Limitação: essa API envia dados como strings ou matrizes byte[]. Para estruturas de dados mais complicadas, como objetos JSON, é necessário serializar para um desses formatos e desserializar do outro lado para reconstruir a estrutura de dados.
Exemplo de uso:
Para entender a sequência completa de uma troca de mensagens bidirecional, os eventos seguem esta ordem:
- Iniciação (app): o app nativo registra o listener com
addWebMessageListenere inicia a navegação nas páginas (como comWebViewCompat.navigateouloadUrl). - Envio de mensagens (Web): as chamadas JavaScript da página da Web
myObject.postMessage(message)para iniciar a comunicação. - Recebimento e resposta de mensagens (app): o app recebe a mensagem no
callback do listener e responde usando o
replyProxy.postMessage()fornecido. - Recebimento de resposta (Web): a página da Web recebe a resposta assíncrona na função de callback
myObject.onmessage().
Kotlin
val myListener = WebViewCompat.WebMessageListener { _, _, _, _, replyProxy ->
// Handle the message from JS
replyProxy.postMessage("Acknowledged!")
}
// Check whether the WebView version supports the feature.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
val allowedOrigins = setOf("https://www.example.com")
WebViewCompat.addWebMessageListener(webView, "myObject", allowedOrigins, myListener)
}
Java
WebMessageListener myListener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {
// Handle the message from JS
replyProxy.postMessage("Acknowledged!");
};
// Check whether the WebView version supports the feature.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
Set<String> allowedOrigins = Set.of("https://www.example.com");
WebViewCompat.addWebMessageListener(webView, "myObject", allowedOrigins, myListener);
}
O JavaScript a seguir demonstra a implementação do lado do cliente de
addWebMessageListener, permitindo que o conteúdo da Web receba mensagens do
app nativo e envie as próprias mensagens pelo proxy myObject.
myObject.onmessage = function(event) {
console.log("App says: " + event.data);
};
myObject.postMessage("Hello world!");
Use postWebMessage (alternativa)
O Android introduziu isso para oferecer uma alternativa assíncrona baseada em mensagens
semelhante ao window.postMessage da Web.
Como funciona: o app usa WebViewCompat.postWebMessage para enviar uma carga útil
ao frame principal da página da Web. Para estabelecer um canal de comunicação bidirecional, crie um WebMessageChannel e transmita uma das portas dele com a mensagem ao conteúdo da Web.
Características:
- Assíncrono: assim como
addWebMessageListener, esse método usa mensagens assíncronas, o que garante que a página da Web permaneça responsiva às interações do usuário enquanto o app processa dados em segundo plano. - Com reconhecimento de origem: é possível especificar um
targetOriginpara garantir que a WebView forneça dados apenas a um site confiável.
Limitações:
- Escopo: essa API limita a comunicação ao frame principal. Ele não é compatível com o envio de mensagens ou o direcionamento direto a iframes.
- Restrições de URI: não é possível usar esse método para conteúdo carregado com URIs
data:,file:ouloadData(), a menos que você especifique "*" como a origem de destino. Assim, qualquer página pode receber a mensagem. - Risco de identidade: não há uma maneira clara de o conteúdo da Web verificar a identidade do remetente. Uma mensagem recebida pela página da Web pode ter sido originada do seu app nativo ou de outro iframe.
Use esse método quando precisar de um canal assíncrono simples para dados baseados em strings em
versões anteriores do Android que não oferecem suporte a addWebMessageListener.
Usar addJavascriptInterface (legado)
O método mais antigo envolve injetar uma instância de objeto nativo diretamente na WebView.
Como funciona: você define uma classe Kotlin ou Java, anota os métodos permitidos com @JavascriptInterface e adiciona uma instância da classe à WebView usando addJavascriptInterface(Object, String).
Características:
- Síncrono: o ambiente de execução do JavaScript é bloqueado até que o método no seu código Android seja retornado.
- Segurança de linhas de execução: o sistema chama métodos em uma linha de execução em segundo plano, exigindo sincronização cuidadosa no lado do Kotlin ou do Java.
- Risco de segurança: por padrão,
addJavascriptInterfaceestá disponível para todos os frames na WebView, incluindo iframes. Ela não tem controle de acesso baseado na origem. Devido ao comportamento assíncrono da WebView, não é possível determinar com segurança o URL do frame que está chamando sua interface. Não confie em métodos comoWebView.getUrl()para verificação de segurança, porque eles não são precisos e não indicam qual frame específico fez a solicitação.
Conversões e coerção de tipo de dados
Ao usar addJavascriptInterface, a ponte Java baseada no Chromium converte
tipos de dados entre o ambiente de execução do JavaScript e o código do app Android.
As seguintes regras de coerção se aplicam aos parâmetros de método e aos valores de retorno.
Mapeamento de tipo de parâmetro (JavaScript para Java)
Quando o JavaScript transmite argumentos para um método anotado em Java ou Kotlin, a ponte força os valores do JavaScript aos tipos de parâmetros Java correspondentes:
| Tipo de parâmetro Java | Valor do argumento JavaScript | Comportamento de coerção |
|---|---|---|
byte, short, int, long |
Número (inteiro) | Os valores são convertidos para o tipo de número inteiro de destino. Valores fora dos limites são ajustados de acordo com as regras padrão de conversão numérica. |
byte, short, int, long |
NaN |
Força a 0. |
byte, short, int, long |
Infinity |
Força para -1 para byte e short ou Integer.MAX_VALUE e Long.MAX_VALUE para int e long. |
float, double |
Número | Força a conversão para o valor de ponto flutuante Java correspondente. |
float, double |
NaN / Infinity |
Força a conversão para Float.NaN, Double.NaN, Float.POSITIVE_INFINITY ou Double.POSITIVE_INFINITY. |
char |
Número (inteiro) | Convertido para o ponto de código Unicode correspondente. |
char |
Não inteiro, NaN, Infinity |
Força a \u0000. |
boolean |
true / false |
Força a conversão para Java true ou false. |
boolean |
Number, String, Object | Força a conversão para false (incluindo strings não vazias e números diferentes de zero). |
String |
String | O valor da string é preservado. |
String |
Número, booleano | Formatado como uma representação de string (por exemplo, "42", "true", "false"). |
String |
null / undefined |
null é convertido para null em Java, e undefined é convertido para a string literal "undefined". |
String |
Object, ArrayBuffer, TypedArray | Força a string literal "undefined". |
Matriz primitiva (como int[], byte[], boolean[]) ou String[] |
Matriz ([...]) |
Converte em uma matriz Java 1D do tipo de elemento de destino. As matrizes esparsas preenchem os índices não atribuídos com valores padrão (0, false, null). |
Matriz primitiva (como int[], byte[]) |
TypedArray (Int8Array, Uint8Array, Int32Array, Float64Array) |
Os elementos são convertidos na matriz primitiva Java correspondente. |
Matriz multidimensional (como int[][]) |
Matriz aninhada ([[...]]) |
incompatível Parâmetros de matriz multidimensional são avaliados como null. |
ArrayBuffer, DataView |
ArrayBuffer, DataView |
Indisponível como matrizes. As instâncias ArrayBuffer e DataView são avaliadas como null. |
Object ou classe personalizada |
Objeto JavaScript ({...}) |
incompatível Literais de objetos JavaScript arbitrários são avaliados como null em Java. |
Object ou classe personalizada |
Wrapper de objeto Java injetado | Compatível (ida e volta). Transmite a instância Java subjacente ao método Java. Gera uma exceção JavaScript se o tipo Java não corresponder à assinatura do parâmetro. |
Tipos de caixa (como Integer, Double, Boolean) |
Número, booleano | incompatível Tipos primitivos em caixa são tratados como objetos opacos e avaliados como null. |
| Qualquer tipo primitivo | null / undefined |
Força os valores padrão (0, 0.0, \u0000, false). |
Object, String, array |
null |
Faz coerção para null do Java. |
Mapeamento de tipo de retorno (Java para JavaScript)
Quando um método anotado em Java ou Kotlin retorna um valor, a ponte o converte em um tipo JavaScript:
| Tipo de retorno do Java | Valor JavaScript | JavaScript typeof |
|---|---|---|
boolean |
true / false |
"boolean" |
byte, short, int, long, float, double |
Número | "number" |
char |
Número (ponto de código Unicode) | "number" |
String (não nulo) |
Valor de string | "string" |
String (null) |
undefined |
"undefined" |
void |
undefined |
"undefined" |
Matriz Java (como int[], String[]) |
undefined |
"undefined". Não é possível usar valores de retorno de matriz. O método Java não é executado, e undefined é retornado sem gerar uma exceção. |
| Objeto Java / tipo personalizado (não nulo) | Wrapper de objeto | "object". Cria um wrapper JavaScript na instância Java. O código JavaScript pode chamar qualquer método público nesse objeto que seja anotado com @JavascriptInterface. |
Objeto Java / tipo personalizado (null) |
null |
"object" |
Primitivo em caixa (como Integer, Double) |
Wrapper de objeto | "object". Retornado como um wrapper de objeto Java opaco sem métodos @JavascriptInterface acessíveis, o que torna o valor inutilizável em JavaScript. |
Acessibilidade de métodos e membros
A ponte JavaScript impõe regras estritas de acesso e visibilidade de membros para proteger contra a execução de código não intencional:
- Os campos não são expostos: os campos Java (incluindo
publicepublic final) não podem ser acessados pelo JavaScript e são avaliados comoundefined. - Requisito de anotação: somente métodos explicitamente anotados com
@JavascriptInterfacesão expostos ao JavaScript. - Restrições de visibilidade: os métodos precisam ser
public. Os métodosprivateeprotectednunca são expostos ao JavaScript, mesmo que tenham a anotação@JavascriptInterface. - Métodos estáticos: métodos estáticos anotados com
@JavascriptInterfacepodem ser chamados do JavaScript. - Herança e substituição: as anotações
@JavascriptInterfacenão são herdadas quando uma subclasse substitui um método. Se uma subclasse substituir um método anotado de uma superclasse, ela precisará incluir explicitamente a anotação@JavascriptInterfaceno método substituído para expô-lo ao JavaScript. Os métodos públicos não substituídos herdados de uma superclasse permanecem acessíveis se forem anotados na superclasse. - Proteção contra reflexão: os métodos padrão de reflexão do Java (como
getClass()) são bloqueados e geram uma exceção do JavaScript para evitar vulnerabilidades de execução remota de código. - Sobrecarga de método: métodos Java sobrecarregados são compatíveis. A ponte resolve chamadas de método com base apenas no número de argumentos transmitidos e não considera os tipos de argumentos. Chamar um método sobrecarregado com um número de argumentos inválido gera uma exceção de JavaScript. Se duas sobrecargas tiverem o mesmo número de argumentos, uma será escolhida arbitrariamente.
Resumo dos mecanismos
A tabela a seguir oferece uma comparação rápida dos três principais mecanismos de implementação de ponte nativa:
| Método | addWebMessageListener |
postWebMessage |
addJavascriptInterface |
|---|---|---|---|
| Implementação | Assíncrono (listener na linha de execução principal) | Assíncrono | Síncrona |
| Segurança | Mais alta (com base na lista de permissões) | Alta (com reconhecimento de origem) | Baixa (sem verificações de origem) |
| Complexidade | Moderado | Moderado | Simples |
| Direção | Bidirecional | Bidirecional | Web para app |
| Versão mínima do WebView | Versão 82 (e Jetpack Webkit 1.3.0) | Versão 45 (e Jetpack Webkit 1.1.0) | Todas as versões |
| Recomendado | Sim | Não | Não |
Processar grandes transferências de dados
Gerencie a memória com cuidado ao transferir payloads grandes, como strings de vários megabytes ou arquivos binários, para evitar erros do tipo "O app não está respondendo" (ANR) ou falhas em dispositivos de 32 bits. Esta seção discute as várias técnicas e limitações associadas à transferência de grandes quantidades de dados entre o aplicativo host e o conteúdo da Web.
Transferir dados binários com matrizes de bytes
Com a classe WebMessageCompat, é possível enviar matrizes byte[] diretamente
em vez de serializar dados binários em strings Base64. Como o Base64 adiciona aproximadamente 33% de sobrecarga ao tamanho dos dados, isso é significativamente mais eficiente em termos de memória e mais rápido.
- Vantagem binária: transfira dados binários, como arquivos de imagem ou áudio, entre seu app nativo e o conteúdo da Web.
- Limitação: mesmo com matrizes de bytes, o sistema copia dados na fronteira de comunicação entre processos (IPC, na sigla em inglês) entre o app e o processo isolado que o WebView usa para renderizar o conteúdo da Web. Isso ainda consome muita memória para arquivos muito grandes.
Os exemplos de código a seguir demonstram como configurar addWebMessageListener no
app nativo para receber mensagens marcadas com
WebMessageCompat.TYPE_ARRAY_BUFFER e, opcionalmente, responder com dados binários
verificando WebViewFeature.MESSAGE_ARRAY_BUFFER.
Kotlin
fun setupWebView(webView: WebView) {
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
val listener = WebViewCompat.WebMessageListener { view, message, sourceOrigin, isMainFrame, replyProxy ->
// Check if the received message is an ArrayBuffer
if (message.type == WebMessageCompat.TYPE_ARRAY_BUFFER) {
val binaryData: ByteArray = message.arrayBuffer
// Process your binary data (image, audio, etc.)
println("Received bytes: ${binaryData.size}")
// Optional: Send a binary reply back to JavaScript.
// This example sends a 3-byte array for simplicity.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)) {
val replyBytes = byteArrayOf(0x01, 0x02, 0x03)
replyProxy.postMessage(replyBytes)
}
}
}
// "myBridge" matches the window.myBridge in JavaScript
WebViewCompat.addWebMessageListener(
webView,
"myBridge",
setOf("https://example.com"), // Security: restrict origins
listener
)
}
}
Java
public void setupWebView(WebView webView) {
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
WebViewCompat.WebMessageListener listener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {
// Check if the received message is an ArrayBuffer
if (message.getType() == WebMessageCompat.TYPE_ARRAY_BUFFER) {
byte[] binaryData = message.getArrayBuffer();
// Process your binary data (image, audio, etc.)
System.out.println("Received bytes: " + binaryData.length);
// Optional: Send a binary reply back to JavaScript.
// This example sends a 3-byte array for simplicity.
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)) {
byte[] replyBytes = new byte[]{0x01, 0x02, 0x03};
replyProxy.postMessage(replyBytes);
}
}
};
// "myBridge" matches the window.myBridge in JavaScript
WebViewCompat.addWebMessageListener(
webView,
"myBridge",
Set.of("https://example.com"), // Security: restrict origins
listener
);
}
}
O código JavaScript a seguir demonstra a implementação do lado do cliente de
addWebMessageListener, permitindo que o conteúdo da Web envie e receba dados
binários (ArrayBuffer) para e do app nativo usando o proxy window.myBridge
inserido no exemplo anterior.
// Function to send an image or binary buffer to the app
async function sendBinaryToApp() {
const response = await fetch('image.jpg');
const buffer = await response.arrayBuffer();
// Check if the injected bridge object exists
if (window.myBridge) {
// You can send the ArrayBuffer directly
window.myBridge.postMessage(buffer);
}
}
// Receiving binary data from the app
if (window.myBridge) {
window.myBridge.onmessage = function(event) {
if (event.data instanceof ArrayBuffer) {
console.log('Received binary data from App, length:', event.data.byteLength);
// Process the binary data (for example, as a Uint8Array)
const bytes = new Uint8Array(event.data);
console.log('First byte:', bytes[0]);
}
};
}
Carregamento eficiente de dados em grande escala
Para arquivos muito grandes (mais de 10 MB), use o método shouldInterceptRequest para
transmitir dados:
- A página da Web inicia uma chamada
fetch()para um URL personalizado de marcador de posição. Por exemplo,https://app.local/large-file. - O app Android intercepta essa solicitação em
WebViewClient.shouldInterceptRequest. - O app retorna os dados como um
InputStream.
Isso permite transmitir dados em partes em vez de carregar todo o payload na memória de uma só vez.
A função JavaScript a seguir demonstra o código do lado do cliente para carregar com eficiência um arquivo binário grande do app nativo usando uma chamada fetch() padrão para um URL personalizado de marcador de posição.
async function fetchBinaryFromApp() {
try {
// This URL doesn't need to exist on the internet
const response = await fetch('https://app.local/data/large-file.bin');
if (!response.ok) throw new Error('Network response was not okay');
// For raw binary data:
const arrayBuffer = await response.arrayBuffer();
console.log('Received binary data, size:', arrayBuffer.byteLength);
// Process buffer (for example, new Uint8Array(arrayBuffer))
/*
// OR for an image:
const blob = await response.blob();
const imageUrl = URL.createObjectURL(blob);
document.getElementById('myImage').src = imageUrl;
*/
} catch (error) {
console.error('Fetch error:', error);
}
}
Os exemplos de código a seguir demonstram o lado do app nativo, usando o
método WebViewClient.shouldInterceptRequest em Kotlin e Java, para transmitir
um arquivo binário grande interceptando um URL de marcador de posição personalizado solicitado pelo
conteúdo da Web.
Kotlin
webView.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
val url = request?.url ?: return null
// Check if this is our custom placeholder URL
if (url.host == "app.local" && url.path == "/data/large-file.bin") {
try {
// 1. Get your data as an InputStream
// (from Assets, Files, or a generated byte stream)
val inputStream: InputStream = context.assets.open("my_data.pb")
// 2. Define Response Headers (Crucial for CORS/Fetch)
val headers = mutableMapOf<String, String>()
headers["Access-Control-Allow-Origin"] = "*" // Allow fetch from any origin
// 3. Return the response
return WebResourceResponse(
"application/octet-stream", // MIME type (for example, image/jpeg)
"UTF-8", // Encoding
200, // Status Code
"OK", // Reason Phrase
headers, // Custom Headers
inputStream // The actual data stream
)
} catch (e: Exception) {
// Handle exception
}
}
return super.shouldInterceptRequest(view, request)
}
}
Java
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
String urlPath = request.getUrl().getPath();
String host = request.getUrl().getHost();
// Check if this is our custom placeholder URL
if ("app.local".equals(host) && "/data/large-file.bin".equals(urlPath)) {
try {
// 1. Get your data as an InputStream
// (from Assets, Files, or a generated byte stream)
InputStream inputStream = getContext().getAssets().open("my_data.pb");
// 2. Define Response Headers (Crucial for CORS/Fetch)
Map<String, String> headers = new HashMap<>();
headers.put("Access-Control-Allow-Origin", "*"); // Allow fetch from any origin
// 3. Return the response
return new WebResourceResponse(
"application/octet-stream", // MIME type (for example, image/jpeg)
"UTF-8", // Encoding
200, // Status Code
"OK", // Reason Phrase
headers, // Custom Headers
inputStream // The actual data stream
);
} catch (Exception e) {
// Handle exception
}
}
return super.shouldInterceptRequest(view, request);
}
});
Siga as recomendações de segurança
Para proteger seu aplicativo e os dados do usuário, siga estas diretrizes ao implementar uma ponte:
Aplicar HTTPS: para garantir que conteúdo de terceiros mal-intencionado não possa invocar a lógica nativa do seu aplicativo, permita apenas a comunicação com origens seguras.
Confie nas regras de origem: a melhor maneira de lidar com a confiança é definir estritamente seu
allowedOriginRulese verificar osourceOriginfornecido no callback da mensagem. Evite usar o caractere curinga completo (*), que corresponde a todas as origens, como sua única regra de origem, a menos que seja absolutamente necessário. O uso de caracteres curinga para subdomínios (por exemplo,*.example.com) continua válido e seguro para corresponder a vários subdomínios (por exemplo,foo.example.com,bar.example.com).Observação: embora as regras de origem protejam contra sites mal-intencionados de terceiros e iframes ocultos, elas não podem proteger contra vulnerabilidades de scripting em vários sites (XSS) no seu próprio domínio confiável. Por exemplo, se a página da Web mostrar conteúdo gerado pelo usuário e estiver vulnerável a XSS armazenado, um invasor poderá executar um script agindo como sua origem confiável. Considere aplicar a validação aos payloads de mensagens antes de executar operações sensíveis da plataforma nativa.
Minimizar a área de superfície: exponha apenas os métodos ou dados específicos que a página da Web exige.
Verificar recursos no ambiente de execução: as APIs de ponte recentes, incluindo
addWebMessageListener, fazem parte da biblioteca Jetpack Webkit. Portanto, sempre verifique se há suporte usandoWebViewFeature.isFeatureSupported()antes de ligar para eles.