---
title: "In-App Review e In-App Updates no Android com Kotlin em 2026"
url: "https://kotlin.dev.br/blog/in-app-review-update-play-core-android-kotlin-2026/"
markdown_url: "https://kotlin.dev.br/blog/in-app-review-update-play-core-android-kotlin-2026.MD"
description: "Aprenda a usar In-App Review e In-App Updates com Play Core em Kotlin: ReviewManager, update flexível e imediato, listeners, quotas de review e boas práticas."
date: "2026-09-17"
author: "Karina Melo"
---

# In-App Review e In-App Updates no Android com Kotlin em 2026

Aprenda a usar In-App Review e In-App Updates com Play Core em Kotlin: ReviewManager, update flexível e imediato, listeners, quotas de review e boas práticas.


**Resposta rápida:** para pedir avaliação do seu app use a **In-App Review API** (`ReviewManager`) e nunca mais jogue o usuário na página da Play Store; para empurrar a versão nova use a **In-App Updates API** (`AppUpdateManager`) com fluxo **flexível** (download em segundo plano + reinício conveniente) ou **imediato** (tela de bloqueio, obrigatório para atualizações críticas). As duas APIs fazem parte do kit **Play Core** (`com.google.android.play:review-ktx` e `com.google.android.play:app-update-ktx`) e só funcionam em apps distribuídos pelo Google Play, já assinados com a chave de release ou em builds internos de teste. Não implemente review em um botão "Avalie-nos" que dispara toda hora — o Google controla as quotas e o diálogo simplesmente não aparece.

## O que cada API resolve

As duas APIs cobrem os dois momentos mais delicados da vida de um app Android: a nota que o usuário dá e a versão que ele roda. Ambas foram criadas porque o anti-padrão clássico — abrir o link `market://details?id=...` — derruba o usuário do fluxo, tem conversão baixíssima e não permite atualizar nada.

| Necessidade | API | Componente | O que o usuário vê |
|---|---|---|---|
| Pedir avaliação ou nota | In-App Review | `ReviewManager` | Diálogo nativo da Play Store sobre o app |
| Atualizar com opção de adiamento | In-App Updates (flexível) | `AppUpdateManager` | Download em background + snack/dialog para reiniciar |
| Atualizar sem opção de adiamento | In-App Updates (imediato) | `AppUpdateManager` | Tela em tela cheia com barra de progresso |
| Testar fora do ciclo real | Play Core Testing | ReviewManager fake / `FakeAppUpdateManager` | Mesma UI em builds de teste |

Se seu app não é distribuído pelo Google Play, nenhuma das duas serve — nesse caso, atualização é [backend service](/blog/foreground-service-android-kotlin-2026/) ou notificação, e review é link externo.

## Dependências e configuração mínima

Adicione as bibliotecas do Play Core ao `build.gradle.kts` do módulo:

```kotlin
dependencies {
    implementation("com.google.android.play:review-ktx:2.0.2")
    implementation("com.google.android.play:app-update-ktx:2.1.0")
    implementation("com.google.android.play:core-ktx:1.8.1")
}
```

Para o in-app update, não é necessário declarar `INSTALL_REFERRER` nem qualquer permissão especial: a `AppUpdateManager` depende apenas de o app estar publicado no Google Play e de o dispositivo ter uma versão recente dos serviços da loja.

Um detalhe que economiza horas de debugging: **as duas APIs não funcionam com `debug` assinado com chave de debug em qualquer aparelho**. Elas funcionam em:

1. builds publicados (produção, open/closed test);
2. **Internal app sharing** ou testes internos com assinatura do Play;
3. builds locais assinados com a **chave de release** e testadas com as classes fake do Play Core Testing.

## In-App Review: como pedir avaliação sem irritar

O princípio da API é simples: **você pede, o Google decide**. Você chama o fluxo no momento certo, mas o sistema avalia se mostra o diálogo — inclusive um limite interno de frequência (a documentação fala em algo próximo de uma vez ao mês por app, sem número garantido).

### Fluxo básico

```kotlin
import com.google.android.play.core.review.ReviewManagerFactory
import com.google.android.play.core.review.testing.FakeReviewManager

class ReviewHelper(private val activity: Activity) {

    private val manager = ReviewManagerFactory.create(activity)

    fun pedirAvaliacao() {
        val request = manager.requestReviewFlow()
        request.addOnCompleteListener { task ->
            if (task.isSuccessful) {
                val reviewInfo = task.result
                val flow = manager.launchReviewFlow(activity, reviewInfo)
                flow.addOnCompleteListener {
                    // Fluxo terminou (aprovado OU não mostrado). Siga o app normalmente.
                }
            }
            // Em falha: silêncio. Nunca trate "não mostrou" como erro do usuário.
        }
    }
}
```

Pontos que diferem do senso comum:

- **`ReviewInfo` é perecível.** Só é válido por algumas horas. Peça o `requestReviewFlow` perto do momento de uso, não no `onCreate` do app.
- **Não há callback dizendo se o diálogo apareceu.** O `addOnCompleteListener` dispara nos dois casos. Você não pode saber a nota nem se a avaliação aconteceu.
- **Nunca incentive com recompensa.** Dar moedas por avaliação viola a política da Play Store.

### Quando acionar o pedido

Momentos que funcionam bem: após concluir uma tarefa importante (pedido entregue, treino finalizado, exportação concluída), após N sessões positivas, ou ao fechar um ticket de suporte resolvido. Momentos ruins: no primeiro launch, na abertura do app, no meio de um formulário, imediatamente após um crash. Se o app usa [WorkManager para sincronizar dados](/blog/workmanager-kotlin-android-2026/), o fim da sincronização raramente é um bom gatilho — o usuário nem percebeu valor ainda.

### Testando com FakeReviewManager

```kotlin
val manager = if (BuildConfig.DEBUG)
    FakeReviewManager(context)
else
    ReviewManagerFactory.create(context)
```

O fake sempre mostra o fluxo, o que permite validar a integração e o estado da UI em `debug`. Para validar o comportamento real de quota, use internal app sharing.

## In-App Updates: flexível ou imediato?

A decisão central de qualquer integração. A pergunta que responde tudo: **o app pode continuar rodando na versão antiga sem risco?**

- **Update flexível (`AppUpdateType.FLEXIBLE`):** o download acontece em background; quando termina, você mostra um snack ou botão para o usuário reiniciar quando quiser. Ideal para atualizações normais de features.
- **Update imediato (`AppUpdateType.IMMEDIATE`):** tela de tela cheia com barra de progresso; o usuário precisa atualizar para usar o app. Reserve para correções críticas — crash on launch, vulnerabilidade de segurança, quebra de API do backend.

Aplicar update imediato em toda release é o erro mais comum — e o mais punido em avaliações de usuários.

### Preparando o AppUpdateManager

```kotlin
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.appupdate.AppUpdateInfo
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.UpdateAvailability
import com.google.android.play.core.ktx.AppUpdateResult

class UpdateHelper(private val activity: Activity) {

    private val appUpdateManager = AppUpdateManagerFactory.create(activity)

    fun checarAtualizacao(tipo: Int = AppUpdateType.FLEXIBLE) {
        val request = appUpdateManager.appUpdateInfo
        request.addOnSuccessListener { info: AppUpdateInfo ->
            val disponivel = info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
            val permitido = info.isUpdateTypeAllowed(tipo)
            if (disponivel && permitido) {
                iniciarFluxo(info, tipo)
            }
        }
    }

    private fun iniciarFluxo(info: AppUpdateInfo, tipo: Int) {
        appUpdateManager.startUpdateFlowForResult(
            info,
            tipo,
            activity,
            REQUEST_CODE_UPDATE
        )
    }

    companion object {
        const val REQUEST_CODE_UPDATE = 7001
    }
}
```

A partir das versões KTX recentes, existe também a API suspensa com [coroutines](/blog/coroutines-kotlin/), `appUpdateManager.requestUpdateFlow()`, que devolve um `AppUpdateResult` e se integra naturalmente a um ViewModel — mais idiomático em projetos Kotlin do que os listeners de `Task`.

### Ouvindo o progresso no modo flexível

O update flexível só faz sentido se você retomar o estado depois. Registre um listener para saber quando o download termina:

```kotlin
import com.google.android.play.core.install.InstallState
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.InstallStatus

private val listener = InstallStateUpdatedListener { state: InstallState ->
    when (state.installStatus()) {
        InstallStatus.DOWNLOADED -> mostrarSnackReiniciar()
        InstallStatus.DOWNLOADING -> atualizarProgresso(state.bytesDownloaded(), state.totalBytesToDownload())
        else -> Unit
    }
}

override fun onResume() {
    super.onResume()
    appUpdateManager.registerListener(listener)

    // App pode ter voltado de um update imediato concluído:
    appUpdateManager.appUpdateInfo.addOnSuccessListener { info ->
        if (info.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
            appUpdateManager.startUpdateFlowForResult(
                info,
                AppUpdateType.IMMEDIATE,
                this,
                REQUEST_CODE_UPDATE
            )
        }
    }
}

override fun onPause() {
    super.onPause()
    appUpdateManager.unregisterListener(listener)
}

private fun mostrarSnackReiniciar() {
    Snackbar.make(
        findViewById(R.id.main_content),
        "Nova versão baixada. Reiniciar agora?",
        Snackbar.LENGTH_INDEFINITE
    ).setAction("Reiniciar") {
        appUpdateManager.completeUpdate()
    }.show()
}
```

Dois comportamentos obrigatórios para produção:

1. **`DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS`:** se o processo morreu no meio de um update imediato (usuário fechou, sistema matou), retome o fluxo no `onResume` — o snippet acima cobre.
2. **`completeUpdate()` só no gesto do usuário** no modo flexível. Chamar no background reinicia o app sem aviso e destrói confiança.

### Testing com FakeAppUpdateManager

```kotlin
val fakeManager = FakeAppUpdateManager(context)
fakeManager.setUpdateAvailable(UpdateAvailability.UPDATE_AVAILABLE)
fakeManager.setUpdateAvailableVersionCode(42)
fakeManager.setClientVersionStalenessDays(3)
```

Isso permite testar os dois fluxos em CI e em [testes de screenshot com Compose](/blog/testes-screenshot-compose-kotlin-2026/) sem publicar nada.

## Onde encaixar no ciclo de vida do app

Uma estratégia saudável para apps de produção:

| Evento | Ação |
|---|---|
| Primeiro launch | Nada. Nunca peça review nem update no onboarding |
| Fim de tarefa concluída (2ª+ vez) | Candidato a In-App Review |
| App em uso > X sessões na versão atual | Candidato a update flexível |
| Versão atual marcada como crítica (flag do backend) | Update imediato |
| `onActivityResult` do update imediato | Retomar estado; usuário escolheu atualizar |

Marcar a versão como "crítica" deve vir de configuração de servidor, não hardcoded — assim você consegue suavizar a exigência se descobrir um bug na versão nova. Se o app já expõe um canal de [push com FCM](/blog/fcm-notificacoes-push-android-kotlin-compose-2026/), combiná-lo com o update flexível aumenta muito a taxa de atualização sem recorrer ao bloqueio.

## Erros comuns

1. **Chamar `launchReviewFlow` dentro de um clique de "Avalie-nos 5 estrelas".** A política permite o botão, mas ele deve levar ao fluxo da API ou à listagem da Play — e não pode prometer recompensa. O melhor botão é o que leva à listagem; a API deve disparar sozinha em momentos naturais.
2. **Requisitar o review flow no `onCreate`.** O `ReviewInfo` expira e você ancora a chamada no pior momento possível.
3. **Tratar a ausência do diálogo como erro.** Logar exceção, mostrar toast, pior: redirecionar para a Play Store "de fallback". A quota é por design.
4. **`AppUpdateType.IMMEDIATE` para toda release.** Bloquear usuário por features cosméticas gera reviews ruins e uninstall.
5. **Não retomar `DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS`.** O usuário fica preso em um loop de tela branca no restart.
6. **Esquecer `unregisterListener`.** Vazamento de listener e callbacks fantasma em fragments recriados.
7. **Testar em build com chave de debug e achar que "não funciona".** É comportamento esperado: use as classes fake ou internal app sharing.
8. **Confiar que o update resolve compatibilidade de API antiga.** O backend ainda precisa tolerar versões em defasagem por semanas — trate versão mínima com [alertas de API deprecada](/blog/play-integrity-api-android-kotlin-2026/) no backend, não só no cliente.

## Perguntas frequentes

**In-App Review funciona fora do Google Play?** Não. Em sideload, Amazon Appstore ou APK direto, a API falha silenciosamente. Nesses canais, o caminho é um link para a loja correspondente.

**Posso saber se o usuário avaliou ou quantas estrelas deu?** Não. A API não retorna nota nem confirmação — por privacidade e por design anti-manipulação.

**Quantas vezes o diálogo de review aparece?** O Google não publica o número exato; a orientação oficial é tratar como "esporádico" e nunca depender dele para o único pedido de feedback. Para feedback sempre disponível, abra um canal próprio (ex.: formulário interno ou [Crashlytics + diagnóstico de ANR](/blog/firebase-crashlytics-anr-android-kotlin-2026/) para ouvir falhas antes de pedir nota).

**Update flexível baixa dados móveis?** Sim, o download usa a conexão disponível. Não há controle fino de rede exposto pela API — se isso importa ao seu público, espere Wi-Fi verificando `isActiveNetworkMetered` antes de iniciar o fluxo.

**Preciso dos dois fluxos no mesmo app?** Normalmente sim: flexível como padrão, imediato acionado por flag de versão crítica. Só o imediato só faz sentido em apps com requisito de segurança rígido (banco, por exemplo, como os retratados em [empresas que usam Kotlin no Brasil](/carreira/kotlin-empresas-brasil/)).

**Funciona em Wear OS?** Não. Dispositivos que não suportam a API devolvem falha na `requestReviewFlow`/`appUpdateInfo` — trate como no-op. Para a loja no wearable, veja o guia de [Wear OS com Compose](/blog/wear-os-kotlin-compose-guia-2026/).

## Conclusão

In-App Review e In-App Updates fecham o ciclo de relacionamento com o usuário dentro do próprio app: a nota pedida no momento certo, sem tirar ninguém do fluxo, e a versão nova chegando sem depender da boa vontade diante de um link. A receita é a mesma nas duas: pedir no contexto natural, aceitar que o Google controla a quota, retomar fluxos interrompidos e reservar o update imediato para quando a versão antiga realmente não pode rodar.

Como próximo passo no ciclo de distribuição, avalie [Play Feature Delivery para módulos dinâmicos](/blog/play-feature-delivery-dynamic-feature-kotlin-2026/) para encolher o APK, e [Play Billing para assinaturas e compras](/blog/play-billing-kotlin-compose-assinaturas-2026/) se o app monetiza dentro da loja — junto com Integrity, Review e Updates, esse é o kit completo de Play Core para um app Kotlin maduro em 2026.
