Permission Handler é um componente de aplicativos Android responsável por verificar, solicitar e processar os resultados de permissões em tempo de execução. De acordo com o Android Developer Guide, 2024, um handler de permissões centraliza a lógica de checkSelfPermission, requestPermissions e shouldShowRequestPermissionRationale em uma única classe ou ViewModel. Isso simplifica a manutenção do código e melhora os testes.
Principais pontos
Permission Handler é um padrão arquitetural para gerenciamento centralizado de permissões em tempo de execução do Android. Em vez de chamadas dispersas de ContextCompat.checkSelfPermission e ActivityCompat.requestPermissions por todo o código do aplicativo, toda a lógica de solicitações e processamento de resultados é concentrada em uma única classe. Isso reduz duplicação, simplifica a manutenção e torna o código mais previsível.
A necessidade de um Permission Handler surgiu com a introdução de permissões em tempo de execução no Android 6.0. Antes disso, todas as permissões eram solicitadas no momento da instalação, e o código do aplicativo podia usar qualquer API sem verificações. Após a migração para o modelo de execução, cada uso de uma permissão perigosa requer uma verificação em três etapas: checkSelfPermission, requestPermissions, onRequestPermissionsResult. Espalhar essa lógica entre Activity e Fragment gera duplicação inline e erros. De acordo com o Google I/O 2019, centralizar o tratamento de permissões reduz o número de bugs relacionados a Permission Denial em uma média de 60 por cento.
Um bom Permission Handler fornece uma interface limpa para o código chamador. A Activity ou Fragment não precisa saber os detalhes da solicitação — eles chamam um método como requestCamera(callback), e o próprio handler gerencia a verificação de status, exibição de justificativa, invocação do diálogo do sistema e passagem do resultado para o callback. Isso implementa o princípio de responsabilidade única e separa a lógica de negócios do código de permissões da plataforma.
Um Permission Handler torna-se necessário quando um aplicativo usa 3 ou mais permissões perigosas. Para aplicativos simples com uma permissão (por exemplo, câmera para um scanner de QR code), uma chamada direta pode ser suficiente. Mas para um aplicativo móvel típico com câmera, geolocalização, notificações e armazenamento — um handler centralizado é essencial para a manutenção.
Um Permission Handler típico consiste em três camadas: um contrato de interface, uma implementação com ActivityResultLauncher e uma camada de ViewModel. A interface define métodos de solicitação para cada permissão — requestCamera, requestLocation, requestStorage. A implementação vincula esses métodos aos contratos ActivityResultContracts.RequestPermission correspondentes.
Componentes-chave da arquitetura:
Essa arquitetura permite trocar facilmente as implementações nos testes: em vez de um ActivityResultLauncher real, usa-se um mock que retorna um resultado predefinido sem interação com o sistema. Isso é fundamental para testes unitários da lógica de UI, onde iniciar uma Activity para um diálogo de permissão é impossível.
O Permission Handler deve levar em conta o ciclo de vida da Activity e do Fragment. Os launchers são registrados no ActivityResultRegistry, que salva e restaura automaticamente o estado ao girar a tela ou recriar a Activity. O handler não deve armazenar referências diretas à Activity ou Fragment — em vez disso, use WeakReference ou passe o registry pelo construtor. Isso evita vazamentos de memória e falhas durante mudanças de configuração.
Uma implementação básica de Permission Handler é construída sobre ActivityResultContracts.RequestPermission. O handler recebe o ActivityResultRegistry de ComponentActivity ou Fragment e registra launchers para cada permissão. Cada launcher aceita uma lambda de callback que é invocada após a resposta do usuário.
sealed class PermissionResult {
object GRANTED : PermissionResult()
data class DENIED(
val shouldShowRationale: Boolean
) : PermissionResult()
}
interface PermissionHandler {
fun requestCamera(
callback: (PermissionResult) -> Unit
)
fun requestLocation(
callback: (PermissionResult) -> Unit
)
fun isPermissionGranted(
permission: String
): Boolean
}
class AndroidPermissionHandler(
private val registry: ActivityResultRegistry,
private val context: Context
) : PermissionHandler {
private var cameraLauncher: ActivityResultLauncher<String>? = null
fun initialize() {
cameraLauncher = registry.register(
"camera_permission",
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
pendingCameraCallback?.invoke(
PermissionResult.GRANTED
)
} else {
val rationale = ActivityCompat.shouldShowRequestPermissionRationale(
context as Activity,
Manifest.permission.CAMERA
)
pendingCameraCallback?.invoke(
PermissionResult.DENIED(rationale)
)
}
}
}
private var pendingCameraCallback:
((PermissionResult) -> Unit)? = null
override fun requestCamera(
callback: (PermissionResult) -> Unit
) {
if (isPermissionGranted(
Manifest.permission.CAMERA
)) {
callback.invoke(PermissionResult.GRANTED)
return
}
pendingCameraCallback = callback
cameraLauncher?.launch(
Manifest.permission.CAMERA
)
}
override fun isPermissionGranted(
permission: String
): Boolean {
return ContextCompat.checkSelfPermission(
context, permission
) == PackageManager.PERMISSION_GRANTED
}
}
O handler é inicializado no onCreate da Activity via registerForActivityResult, que fornece acesso ao ActivityResultRegistry. Após a inicialização, o handler está pronto para processar solicitações durante todo o ciclo de vida da Activity. É importante chamar initialize antes da primeira solicitação, caso contrário o launcher não será registrado.
Integrar um Permission Handler com ViewModel é a abordagem mais avançada. O ViewModel gerencia o estado das solicitações, enquanto o Handler apenas realiza chamadas de plataforma. O ViewModel contém StateFlow<PermissionUiState>, onde UiState descreve qual permissão está sendo solicitada e qual resultado foi recebido. A Activity se inscreve neste StateFlow e delega a solicitação ao Handler.
class PermissionsViewModel : ViewModel() {
private val _uiState =
MutableStateFlow<PermissionUiState>(
PermissionUiState.Idle
)
val uiState: StateFlow<PermissionUiState> = _uiState.asStateFlow()
fun onCameraRequested() {
_uiState.value = PermissionUiState.RequestingCamera
}
fun onPermissionResult(
permission: String,
result: PermissionResult
) {
when (result) {
PermissionResult.GRANTED -> {
_uiState.value = PermissionUiState.Granted(permission)
}
is PermissionResult.DENIED -> {
_uiState.value = PermissionUiState.Denied(
permission,
result.shouldShowRationale
)
}
}
}
}
sealed class PermissionUiState {
object Idle : PermissionUiState()
object RequestingCamera : PermissionUiState()
data class Granted(val permission: String) : PermissionUiState()
data class Denied(
val permission: String,
val shouldShowRationale: Boolean
) : PermissionUiState()
}
Neste modelo, a Activity verifica isPermissionGranted através do Handler na inicialização, enquanto o ViewModel apenas gerencia o estado. Se a permissão não for concedida — a Activity se inscreve no uiState, chama requestCamera do Handler e passa o resultado de volta ao ViewModel via onPermissionResult. Separar o código de plataforma da lógica de negócios permite testar o ViewModel sem dependências do Android.
Os testes unitários de Permission Handler são possíveis graças à interface PermissionHandler. Nos testes, um FakePermissionHandler é criado para simular vários cenários: permissão concedida, negada, Never Ask Again. Cada cenário é testado independentemente. Isso é especialmente importante para testar a lógica de UI que deve reagir corretamente a todos os três resultados.
class FakePermissionHandler : PermissionHandler {
var cameraResult: PermissionResult =
PermissionResult.GRANTED
var grantedPermissions: Set<String> =
setOf(Manifest.permission.CAMERA)
override fun requestCamera(
callback: (PermissionResult) -> Unit
) {
callback.invoke(cameraResult)
}
override fun isPermissionGranted(
permission: String
): Boolean {
return permission in grantedPermissions
}
}
A implementação fake permite testar o ViewModel sem um emulador. Basta definir cameraResult para o valor desejado e verificar se o ViewModel atualiza corretamente seu UiState. Os testes de integração verificam o PermissionHandler real com ActivityScenario, mas normalmente há apenas 2-3 desses testes por aplicativo — os cenários restantes são cobertos por testes unitários com fakes.
Os erros típicos ao trabalhar com Permission Handler incluem: não verificar checkSelfPermission antes de cada chamada de API, ignorar shouldShowRequestPermissionRationale, chamar requestPermissions novamente após Never Ask Again e armazenar launchers sem considerar o ciclo de vida da Activity. Vamos examinar cada problema e sua solução.
O erro mais comum é chamar uma API sem verificar o status da permissão. Os desenvolvedores assumem que, se uma permissão foi concedida uma vez, ela permanecerá para sempre. No entanto, o usuário pode revogá-la através das configurações a qualquer momento. Um Permission Handler deve sempre chamar isPermissionGranted antes de realizar uma operação sensível. O segundo erro popular é ignorar shouldShowRequestPermissionRationale e repetir a solicitação, o que leva a uma negação instantânea sem diálogo no modo Never Ask Again.
As melhores práticas incluem: criar uma única instância de Handler para todo o ciclo de vida da Activity, usar SharedFlow para passar resultados ao ViewModel, registrar todas as solicitações e negações para análise e exibir um diálogo de justificativa personalizado antes do diálogo do sistema na primeira negação. Seguir essas regras garante um tratamento estável de permissões em todas as versões do Android.
Perguntas frequentes
Permission Handler é um componente para gerenciamento centralizado de permissões em tempo de execução, encapsulando checkSelfPermission, requestPermissions e shouldShowRequestPermissionRationale. Simplifica a manutenção do código e melhora os testes.
Recomenda-se usar ActivityResultContracts.RequestPermission da biblioteca androidx.activity. Ele substitui o obsoleto onRequestPermissionsResult e fornece uma API de callback limpa com resultado Boolean.
Para uma única permissão, um Handler não é obrigatório — você pode usar uma chamada direta ao launcher RequestPermission na Activity. Um Handler torna-se necessário com 3 ou mais permissões para evitar duplicação de código.
Crie uma interface PermissionHandler e sua implementação fake para testes unitários. O fake retorna resultados predefinidos sem chamadas de sistema. Isso permite testar a ViewModel e a lógica de UI sem emulador.
Após a negação, verifique shouldShowRequestPermissionRationale. Se o método retornar false — o modo Never Ask Again está ativo. O Handler deve retornar PermissionResult.DENIED(false), e a UI deve mostrar um botão para ir às Configurações.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também