Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

Cómo solicitar permisos de la app

Cada app de Android se ejecuta en una zona de pruebas con acceso limitado. Si una app necesita usar recursos o información ajenos a su propia zona de pruebas, debe solicitar el permiso correspondiente. Para especificar que tu app necesita un permiso, debes incluirlo en el manifiesto de la app y, luego, solicitar que el usuario apruebe cada solicitud durante el tiempo de ejecución (en Android 6.0 o versiones posteriores).

En esta página, se describe cómo usar la biblioteca de compatibilidad de Android para buscar permisos y solicitarlos. A partir de Android 6.0 (nivel de API 23), el marco de trabajo de Android proporciona métodos similares, pero el uso de la biblioteca de compatibilidad facilita la compatibilidad con versiones de Android anteriores.

Cómo agregar permisos al manifiesto

En todas las versiones de Android, si quieres declarar que tu app necesita un permiso, debes incluir un elemento <uses-permission> en el manifiesto de tu app, como elemento secundario del elemento <manifest> del nivel superior. Por ejemplo, una app que necesita acceso a Internet incluiría la siguiente línea en su manifiesto:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
            package="com.example.snazzyapp">

        <uses-permission android:name="android.permission.INTERNET"/>
        <!-- other permissions go here -->

        <application ...>
            ...
        </application>
    </manifest>
    

El comportamiento del sistema después de declarar un permiso depende de qué tan sensible sea. Algunos se consideran "normales" por lo que el sistema los otorga inmediatamente después de la instalación. Otros se consideran "peligrosos", por lo que el usuario debe otorgar explícitamente el acceso a tu app. Para obtener más información sobre los diferentes tipos de permisos, consulta Niveles de protección.

Cómo verificar si tu app tiene permisos

Si tu app necesita un permiso peligroso, debes verificar si tienes ese permiso cada vez que realices una operación que lo requiera. A partir de Android 6.0 (nivel de API 23), los usuarios pueden revocar permisos desde cualquier app en cualquier momento, aunque la app esté orientada a un nivel de API inferior. Por lo tanto, aunque la app haya usado la cámara el día anterior, no puede asumir que aún tiene ese permiso actualmente.

Para verificar si tu app tiene un permiso, llama al método ContextCompat.checkSelfPermission(). Por ejemplo, en este fragmento, se muestra cómo verificar si la actividad tiene permiso para realizar operaciones de escritura en el calendario:

Kotlin

    if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission is not granted
    }
    

Java

    if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED) {
        // Permission is not granted
    }
    

Si la app tiene el permiso, el método muestra PERMISSION_GRANTED y la app puede realizar la operación. Si no lo tiene, el método muestra PERMISSION_DENIED y la app debe solicitárselo al usuario de manera explícita.

Cómo solicitar permisos

Cuando tu app recibe PERMISSION_DENIED de checkSelfPermission(), debes solicitar el permiso al usuario. Android proporciona varios métodos para hacerlo (por ejemplo, requestPermissions()), como se muestra en el fragmento de código que aparece debajo. Cuando llamas a estos métodos, aparece un diálogo de Android estándar que no se puede personalizar.

La manera en que este se muestra al usuario depende de la versión de Android que ejecute el dispositivo y de la versión de destino de tu app, como se explica en Descripción general de permisos.

Explica la razón por la cual la app necesita permisos

En algunos casos, puede que quieras ayudar al usuario a comprender por qué tu app necesita un permiso. Por ejemplo, si un usuario inicia una app de fotografía probablemente no se sorprenda si esta le solicita permiso para usar la cámara. Sin embargo, es posible que este no comprenda por qué la app busca acceder a la ubicación o los contactos. Antes de que tu app solicite un permiso, debes considerar proporcionar una explicación al usuario. Recuerda que no es bueno abrumarlo con explicaciones; si lo haces, podría frustrarse y desinstalar la app.

Un enfoque que puedes usar es proporcionar una explicación solo cuando el usuario haya rechazado el permiso anteriormente. Android proporciona un método de utilidad (shouldShowRequestPermissionRationale()) que muestra el valor true si el usuario rechazó la solicitud anteriormente o el false si lo rechazó y seleccionó la opción No volver a preguntar en el diálogo de solicitud del permiso, o si una política de dispositivo lo prohíbe.

Si un usuario intenta usar reiteradas veces una funcionalidad que requiere un permiso, pero sigue rechazando la solicitud del mismo, es probable que no entienda por qué la app lo necesita para brindar esa funcionalidad. En una situación como esta, puede que te convenga mostrar una explicación.

Para obtener asesoramiento adicional sobre cómo crear una buena experiencia del usuario cuando solicites permiso, consulta las Prácticas recomendadas de permisos de la app.

Cómo solicitar los permisos necesarios

Si tu app aún no tiene el permiso que necesita, esta debe llamar a uno de los métodos requestPermissions() para solicitar los permisos correspondientes. Tu app transmite el permiso que desea y también un número entero de código de solicitud que debes especificar a fin de identificar la solicitud de este permiso. Se muestra inmediatamente y, una vez que el usuario responde la solicitud, el sistema llama al método de devolución de llamada de la app con los resultados y transmite el mismo código de solicitud que la app transmitió a requestPermissions().

El siguiente código verifica si la app tiene permiso para leer los contactos del usuario. Si no lo tiene, verifica si debiera mostrar una explicación de por qué lo necesita; si no es necesario, entonces solicita el permiso:

Kotlin

    // Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.
        } else {
            // No explanation needed, we can request the permission.
            ActivityCompat.requestPermissions(thisActivity,
                    arrayOf(Manifest.permission.READ_CONTACTS),
                    MY_PERMISSIONS_REQUEST_READ_CONTACTS)

            // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    } else {
        // Permission has already been granted
    }
    

Java

    // Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.
        } else {
            // No explanation needed; request the permission
            ActivityCompat.requestPermissions(thisActivity,
                    new String[]{Manifest.permission.READ_CONTACTS},
                    MY_PERMISSIONS_REQUEST_READ_CONTACTS);

            // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    } else {
        // Permission has already been granted
    }
    

La solicitud que muestra el sistema describe el grupo de permisos que necesita tu app, pero no el permiso específico.

Nota: Cuando tu app llama a requestPermissions(), el sistema muestra al usuario un cuadro de diálogo estándar. Tu app no puede configurar ni modificar ese cuadro de diálogo. Si necesitas proporcionar información o una explicación al usuario, debes hacerlo antes de llamar a requestPermissions(), como se describe en Cómo explicar la razón por la cual la app necesita permisos.

Cómo controlar la respuesta a la solicitud de permisos

Cuando el usuario responde la solicitud de permiso de tu app, el sistema invoca el método onRequestPermissionsResult() de tu app y le transmite la respuesta del usuario. Esta debe anular ese método para averiguar si se otorgó el permiso. La devolución de llamada recibe el mismo código de solicitud que transmitiste a requestPermissions(). Por ejemplo, si una app solicita acceso de READ_CONTACTS, es posible que tenga el siguiente método de devolución de llamada:

Kotlin

    override fun onRequestPermissionsResult(requestCode: Int,
            permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            MY_PERMISSIONS_REQUEST_READ_CONTACTS -> {
                // If request is cancelled, the result arrays are empty.
                if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return
            }

            // Add other 'when' lines to check for other
            // permissions this app might request.
            else -> {
                // Ignore all other requests.
            }
        }
    }
    

Java

    @Override
    public void onRequestPermissionsResult(int requestCode,
            String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission was granted, yay! Do the
                    // contacts-related task you need to do.
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                return;
            }

            // other 'case' lines to check for other
            // permissions this app might request.
        }
    }
    

El cuadro de diálogo que muestra el sistema describe el grupo de permisos al que tu app necesita acceder, pero no indica el permiso específico. Por ejemplo, si solicitas el permiso READ_CONTACTS, el cuadro de diálogo del sistema solo dice que tu app necesita acceder a los contactos del dispositivo. El usuario solo debe otorgar el permiso una vez para cada grupo de permisos. Si tu app solicita otros permisos de ese grupo (indicados en el manifiesto de tu app), el sistema los otorga automáticamente. Cuando solicitas el permiso, el sistema llama a tu método de devolución de llamada onRequestPermissionsResult() y transmite PERMISSION_GRANTED, de la misma manera en que lo haría si el usuario hubiera aceptado explícitamente tu solicitud por medio del cuadro de diálogo del sistema.

Nota: De todos modos, tu app debe solicitar explícitamente cada uno de los permisos que necesita aunque el usuario haya otorgado otro del mismo grupo. Además, la agrupación de permisos puede modificarse en futuras versiones de Android. En tu código no debe presuponerse que habrá o no permisos específicos en el mismo grupo.

Por ejemplo, supongamos que incluyes los permisos READ_CONTACTS y WRITE_CONTACTS en el manifiesto de tu app. Si solicitas el permiso READ_CONTACTS y el usuario lo otorga y, luego, solicitas WRITE_CONTACTS, el sistema inmediatamente te lo otorgará sin interactuar con el usuario.

Si un usuario rechaza una solicitud de permiso, tu app debe tomar una medida adecuada. Por ejemplo, esta podría mostrar un diálogo en el que se explique por qué no podría realizar la acción solicitada por el usuario para la cual se requiere ese permiso.

Cuando el sistema solicita al usuario que otorgue un permiso, este tiene la opción de indicar al sistema que no vuelva a solicitarlo. En ese caso, cada vez que la app use requestPermissions() para volver a solicitar ese permiso, el sistema lo rechazará inmediatamente. El sistema llama a tu método de devolución de llamada onRequestPermissionsResult() y transmite PERMISSION_DENIED, de la misma manera que lo haría si el usuario hubiera vuelto a rechazar explícitamente la solicitud. También muestra false si una política de dispositivo prohíbe que la app tenga ese permiso, lo que implica que cuando llamas a requestPermissions(), no puedes suponer que haya existido interacción directa con el usuario.

Si quieres proporcionar la mejor experiencia del usuario al solicitar permisos de la app, también puedes consultar las Prácticas recomendadas de permisos de la app.

Cómo declarar permisos por nivel de API

Para declarar permisos únicamente en dispositivos que admiten permisos durante el tiempo de ejecución (es decir, que ejecuten Android 6.0 [nivel de API 23] o versiones posteriores), debes incluir la etiqueta uses- permission-sdk-23 en lugar de uses-permission.

Cuando usas cualquiera de estas etiquetas, puedes establecer el atributo maxSdkVersion para especificar que, en dispositivos que ejecutan versiones posteriores, no se necesita ningún permiso en particular.

Recursos adicionales

Para obtener información adicional sobre permisos, consulta los siguientes artículos:

Para obtener más información sobre cómo solicitar permisos, descarga las siguientes apps de ejemplo: