Android 4.2 API

Nivel de API: 17

Android 4.2 (JELLY_BEAN_MR1) es una actualización de la versión de Jelly Bean que ofrece funciones nuevas para usuarios y desarrolladores de apps. En este documento, se proporciona una introducción a las APIs nuevas más notables y útiles para los desarrolladores.

Como desarrollador de apps, debes descargar la imagen del sistema Android 4.2 y la plataforma de SDK de SDK Manager lo antes posible. Si no tienes un dispositivo con Android 4.2 en el que probar la app, usa la imagen del sistema de Android 4.2 para probarla en Android Emulator. Luego, compila tus apps con la plataforma Android 4.2 para comenzar a usar las APIs más recientes.

A fin de optimizar mejor tu app para los dispositivos que ejecutan Android 4.2, debes establecer tu targetSdkVersion en "17", instalarla en una imagen del sistema de Android 4.2, probarla y, luego, publicar una actualización con este cambio.

Puedes usar APIs en Android 4.2 y, al mismo tiempo, admitir versiones anteriores. Para ello, debes agregar condiciones a tu código que verifiquen el nivel de API del sistema antes de ejecutar las APIs no compatibles con tu minSdkVersion. Para obtener más información sobre cómo mantener la retrocompatibilidad, consulta Cómo crear IU retrocompatibles.

Puedes obtener más información sobre el funcionamiento de los niveles de API en la sección ¿Qué es un nivel de API?

Importantes cambios en los comportamientos

Si publicaste anteriormente una app para Android, ten en cuenta los siguientes cambios que podrían afectar el comportamiento de tu app:

  • Los proveedores de contenido ya no se exportan de forma predeterminada. Es decir, el valor predeterminado del atributo android:exported ahora es “false". Si es importante que otras apps puedan acceder a tu proveedor de contenido, ahora debes configurar explícitamente android:exported="true".

    Este cambio solo se aplica si estableces android:targetSdkVersion o android:minSdkVersion en 17 o un valor superior. De lo contrario, el valor predeterminado sigue siendo “true", incluso cuando se ejecuta en Android 4.2 y versiones posteriores.

  • En comparación con las versiones anteriores de Android, los resultados de la ubicación del usuario pueden ser menos precisos si tu app solicita el permiso ACCESS_COARSE_LOCATION, pero no el ACCESS_FINE_LOCATION.

    Para cumplir con las expectativas de privacidad de los usuarios cuando tu app solicita permiso para acceder a la ubicación aproximada (y no precisa), el sistema no proporcionará una estimación de la ubicación del usuario más precisa que una manzana.

  • Algunos parámetros de configuración del dispositivo definidos por Settings.System ahora son de solo lectura. Si tu app intenta escribir cambios en la configuración definida en Settings.System que se movieron a Settings.Global, la operación de escritura fallará silenciosamente cuando se ejecute en Android 4.2 y versiones posteriores.

    Incluso si el valor de android:targetSdkVersion y android:minSdkVersion es inferior a 17, la app no podrá modificar los parámetros de configuración que se movieron a Settings.Global cuando se ejecute en Android 4.2 y versiones posteriores.

  • Si tu app usa WebView, Android 4.2 agrega una capa adicional de seguridad para que puedas vincular JavaScript a tu código de Android de manera más segura. Si configuras tu targetSdkVersion en 17 o un valor superior, ahora debes agregar la anotación @JavascriptInterface a cualquier método que desees que esté disponible para tu JavaScript (el método también debe ser público). Si no proporcionas la anotación, una página web no podrá acceder al método en tu WebView cuando se ejecute en Android 4.2 o versiones posteriores. Si configuras targetSdkVersion en 16 o un valor inferior, la anotación no es obligatoria, pero te recomendamos que actualices la versión de destino y agregues la anotación para mayor seguridad.

    Obtén más información sobre la vinculación del código JavaScript con el código de Android.

Daydream

Daydream es un nuevo modo de protector de pantalla interactivo para dispositivos Android. Se activa automáticamente cuando el dispositivo se inserta en un conector o cuando está inactivo mientras está conectado a un cargador (en lugar de apagar la pantalla). Daydream muestra un sueño a la vez, que puede ser una pantalla pasiva puramente visual que se descarta al tacto, o puede ser interactiva y responsiva al conjunto completo de eventos de entrada. Tus sueños se ejecutan en el proceso de tu app y tienen acceso completo al kit de herramientas de IU de Android, incluidas vistas, diseños y animaciones, por lo que son más flexibles y potentes que los fondos de pantalla animados o los widgets de apps.

Puedes crear un sueño para Daydream implementando una subclase de DreamService. Las APIs de DreamService están diseñadas para ser similares a las de Activity. Para especificar la IU de tu sueño, pasa un ID de recurso de diseño o View a setContentView() en cualquier momento después de que haya una ventana, por ejemplo, desde la devolución de llamada onAttachedToWindow().

La clase DreamService proporciona otros métodos importantes de devolución de llamada de ciclo de vida además de las APIs básicas de Service, como onDreamingStarted(), onDreamingStopped() y onDetachedFromWindow(). No puedes iniciar un DreamService desde tu app; el sistema lo inicia automáticamente.

Si tu sueño es interactivo, puedes iniciar una actividad para enviar al usuario a la IU completa de tu app a fin de obtener más detalles o control. Puedes usar finish() para finalizar el sueño de modo que el usuario pueda ver la actividad nueva.

Para que tu daydream esté disponible para el sistema, declara tu DreamService con un elemento <service> en el archivo de manifiesto. Luego, debes incluir un filtro de intents con la acción "android.service.dreams.DreamService". Por ejemplo:

<service android:name=".MyDream" android:exported="true"
    android:icon="@drawable/dream_icon" android:label="@string/dream_label" >
    <intent-filter>
        <action android:name="android.service.dreams.DreamService" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

Existen otros métodos útiles en DreamService que debes tener en cuenta:

  • setInteractive(boolean) controla si el sueño recibe eventos de entrada o si finaliza inmediatamente después de la entrada del usuario. Si el sueño es interactivo, el usuario puede usar los botones Atrás o Inicio para salir del sueño, o puedes llamar a finish() para detenerlo.
  • Si quieres una pantalla completamente envolvente, puedes llamar a setFullscreen() para ocultar la barra de estado.
  • Antes de que se inicie Daydream, la pantalla se atenuará para indicarle al usuario que se acerca el tiempo de espera de inactividad. Si llamas a setScreenBright(true), puedes configurar la pantalla en su brillo habitual.

Para obtener más información, consulta la documentación de DreamService.

Pantallas secundarias

Android ahora permite que tu app muestre contenido único en pantallas adicionales que están conectadas al dispositivo del usuario mediante una conexión con cable o Wi-Fi. Si quieres crear contenido único para una pantalla secundaria, extiende la clase Presentation e implementa la devolución de llamada onCreate(). Dentro de onCreate(), especifica tu IU para la pantalla secundaria llamando a setContentView(). Como extensión de la clase Dialog, la clase Presentation proporciona la región en la que tu app puede mostrar una IU única en la pantalla secundaria.

Para detectar pantallas secundarias en las que puedes mostrar tu Presentation, usa las APIs de DisplayManager o MediaRouter. Si bien las APIs de DisplayManager te permiten enumerar varias pantallas que pueden conectarse a la vez, por lo general, debes usar MediaRouter en su lugar para acceder rápidamente a la pantalla predeterminada del sistema para las presentaciones.

Para obtener la pantalla predeterminada de tu presentación, llama a MediaRouter.getSelectedRoute() y pásale ROUTE_TYPE_LIVE_VIDEO. Se mostrará un objeto MediaRouter.RouteInfo que describe la ruta seleccionada actualmente del sistema para presentaciones en video. Si MediaRouter.RouteInfo no es nulo, llama a getPresentationDisplay() para obtener el Display que representa la pantalla conectada.

Luego, puedes mostrar tu presentación pasando el objeto Display a un constructor para tu clase Presentation. Tu presentación ahora aparecerá en la pantalla secundaria.

Para detectar en el tiempo de ejecución si se conectó una nueva pantalla, crea una instancia de MediaRouter.SimpleCallback en la que implementes el método de devolución de llamada onRoutePresentationDisplayChanged(), al que el sistema llamará cuando se conecte una nueva pantalla de presentación. Luego, registra el MediaRouter.SimpleCallback. Para ello, pásalo a MediaRouter.addCallback() junto con el tipo de ruta ROUTE_TYPE_LIVE_VIDEO. Cuando recibas una llamada a onRoutePresentationDisplayChanged(), simplemente llama a MediaRouter.getSelectedRoute() como se mencionó anteriormente.

Para optimizar aún más la IU en tu Presentation para pantallas secundarias, puedes aplicar un tema diferente especificando el atributo android:presentationTheme en la <style> que aplicaste a tu aplicación o actividad.

Ten en cuenta que las pantallas conectadas al dispositivo del usuario suelen tener un tamaño de pantalla más grande y, probablemente, una densidad de pantalla diferente. Debido a que las características de la pantalla pueden ser diferentes, debes proporcionar recursos que estén optimizados específicamente para pantallas tan grandes. Si necesitas solicitar recursos adicionales de tu Presentation, llama a getContext().getResources() para obtener el objeto Resources correspondiente a la pantalla. De esta manera, se proporcionan los recursos adecuados de tu app que son más adecuados para el tamaño y la densidad de la pantalla secundaria.

Para obtener más información y algunas muestras de código, consulta la documentación de la clase Presentation.

Widgets de pantalla bloqueada

Android ahora permite a los usuarios agregar widgets de apps a la pantalla de bloqueo. Para que el widget de la app esté disponible para su uso en la pantalla de bloqueo, agrega el atributo android:widgetCategory a tu archivo en formato XML que especifique el AppWidgetProviderInfo. Este atributo admite dos valores: home_screen y keyguard. De forma predeterminada, el atributo se establece en home_screen para que los usuarios puedan agregar el widget de la app a la pantalla principal. Si quieres que el widget de la app también esté disponible en la pantalla de bloqueo, agrega el valor keyguard:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:widgetCategory="keyguard|home_screen">
</appwidget-provider>

También debes especificar un diseño inicial para el widget de tu app cuando estés en la pantalla de bloqueo con el atributo android:initialKeyguardLayout. Esto funciona de la misma manera que android:initialLayout, ya que proporciona un diseño que puede aparecer inmediatamente hasta que se inicialice el widget de la app y se pueda actualizar el diseño.

Para obtener más información sobre cómo compilar widgets de apps para la pantalla de bloqueo, incluso cómo ajustar el tamaño del widget de la app correctamente en la pantalla de bloqueo, consulta la guía Widgets de apps.

Varios usuarios

Android ahora permite varios espacios de usuarios en dispositivos que se pueden compartir, como tablets. Cada usuario de un dispositivo tiene su propio conjunto de cuentas, apps, configuración del sistema, archivos y cualquier otro dato asociado al usuario.

Como desarrollador de apps, no hay nada diferente que debas hacer para que tu app funcione correctamente con varios usuarios en un solo dispositivo. Sin importar la cantidad de usuarios que pueda haber en un dispositivo, los datos que guarda tu app para un usuario determinado se mantienen separados de los datos que guarda tu app para otros usuarios. El sistema realiza un seguimiento de los datos del usuario que pertenecen al proceso del usuario en el que se ejecuta tu app, y le brinda a tu app acceso solo a los datos de ese usuario y no permite el acceso a los datos de otros usuarios.

Guarda datos en un entorno multiusuario

Cuando tu app guarda las preferencias del usuario, crea una base de datos o escribe un archivo en el espacio de almacenamiento interno o externo del usuario, solo se puede acceder a esos datos mientras se ejecutan como ese usuario.

Para asegurarte de que tu app se comporte correctamente en un entorno multiusuario, no hagas referencia al directorio interno de la app ni a la ubicación de almacenamiento externo con rutas codificadas y, en su lugar, usa las APIs correspondientes:

Sin importar cuál de estas APIs uses para guardar los datos de un usuario determinado, no se podrá acceder a los datos mientras se ejecuten como un usuario diferente. Desde el punto de vista de la app, cada usuario se ejecuta en un dispositivo completamente separado.

Identifica usuarios en un entorno multiusuario

Si tu app quiere identificar usuarios únicos para recopilar estadísticas o crear otras asociaciones de cuentas, debes seguir las prácticas recomendadas para identificar instalaciones únicas. Si creas un UUID nuevo cuando tu app se inicia por primera vez, tienes la certeza de que obtendrás un ID único para hacer un seguimiento de cada usuario, sin importar cuántos usuarios instalen tu app en un solo dispositivo. Como alternativa, puedes guardar un token local recuperado de tu servidor o usar el ID de registro que proporciona Google Cloud Messaging.

Ten en cuenta que, si tu app solicita uno de los identificadores de dispositivos de hardware (como la dirección MAC de Wi-Fi o el número SERIAL), proporcionarán el mismo valor para cada usuario porque estos identificadores están vinculados al hardware y no al usuario. Sin mencionar los otros problemas que generan estos identificadores, como se explica en la entrada de blog Cómo identificar instalaciones de apps.

Nueva configuración global

Con la adición de Settings.Global, se actualizó la configuración del sistema para admitir varios usuarios. Este conjunto de opciones de configuración es similar a la de Settings.Secure porque son de solo lectura, pero se aplican globalmente en todos los espacios de usuario del dispositivo.

Se trasladaron aquí varios parámetros de configuración existentes de Settings.System o Settings.Secure. Si tu app está realizando cambios en la configuración definida previamente en Settings.System (como AIRPLANE_MODE_ON), debes esperar que esta acción ya no funcione en un dispositivo con Android 4.2 o versiones posteriores si esa configuración se trasladó a Settings.Global. Puedes seguir leyendo la configuración que se encuentra en Settings.Global, pero como estos ya no se consideran seguros para que las apps cambien, el intento de hacerlo fallará silenciosamente y el sistema escribirá una advertencia en el registro del sistema cuando ejecute tu app en Android 4.2 o versiones posteriores.

Compatibilidad con diseño RTL

Android ahora ofrece varias APIs que te permiten compilar interfaces de usuario que transforman de forma correcta la orientación del diseño para admitir los idiomas que usan IU de derecha a izquierda (RTL) y la dirección de lectura, como el árabe y el hebreo.

Para comenzar a admitir diseños de derecha a izquierda en tu app, configura el atributo android:supportsRtl en el elemento <application> de tu archivo de manifiesto y establécelo en “true". Una vez que habilites esta opción, el sistema habilitará varias APIs de derecha a izquierda para mostrar tu app con diseños de ese tipo. Por ejemplo, la barra de acciones mostrará el ícono y el título en el lado derecho y los botones de acción en la izquierda, y también se revertirán los diseños que hayas creado con las clases View proporcionadas por el framework.

Si necesitas optimizar aún más el aspecto de tu app cuando se muestra con un diseño de derecha a izquierda, hay dos niveles básicos de optimización:

  1. Convierte las propiedades de diseño orientadas a la izquierda y la derecha en propiedades de diseño orientadas al inicio y al final.

    Por ejemplo, usa android:layout_marginStart en lugar de android:layout_marginLeft y android:layout_marginEnd en lugar de android:layout_marginRight.

    La clase RelativeLayout también proporciona los atributos de diseño correspondientes para reemplazar las posiciones izquierda y derecha, como android:layout_alignParentStart a fin de reemplazar android:layout_alignParentLeft y android:layout_toStartOf en lugar de android:layout_toLeftOf.

  2. O bien, para proporcionar una optimización completa para diseños de derecha a izquierda, puedes proporcionar archivos de diseño completamente independientes mediante el calificador de recursos ldrtl (ldrtl significa dirección del diseño de derecha a izquierda). Por ejemplo, puedes guardar los archivos de diseño predeterminados en res/layout/ y los diseños optimizados para RTL en res/layout-ldrtl/.

    El calificador ldrtl es excelente para recursos de elementos de diseño, de modo que puedas proporcionar gráficos que estén orientados en la dirección correspondiente a la dirección de lectura.

Hay varias otras APIs disponibles en el framework para admitir diseños de derecha a izquierda, como en la clase View, de modo que puedas implementar los comportamientos adecuados para vistas personalizadas, y en Configuration para consultar la dirección actual del diseño.

Nota: Si usas SQlite y los nombres de tablas o columnas son "solo números", ten cuidado: usar String.format(String, Object...) puede generar errores en los que los números se convirtieron a sus equivalentes en árabe si tu dispositivo está configurado en la configuración regional en árabe. Debes usar String.format(Locale,String,Object...) para asegurarte de que los números se conserven como ASCII. También usa String.format("%d", int) en lugar de String.valueOf(int) para darles formato a los números.

Fragmentos anidados

Ahora puedes incorporar fragmentos dentro de fragmentos. Esto resulta útil para una variedad de situaciones en las que quieras colocar componentes de la IU dinámicos y reutilizables en un componente de la IU que sea dinámico y reutilizable. Por ejemplo, si usas ViewPager para crear fragmentos que se deslizan hacia la izquierda y hacia la derecha y ocupan la mayor parte del espacio de la pantalla, ahora puedes insertar fragmentos en cada página de fragmento.

Para anidar un fragmento, simplemente llama a getChildFragmentManager() en el Fragment donde deseas agregar un fragmento. Esto muestra un FragmentManager que puedes usar como lo haces normalmente desde la actividad de nivel superior para crear transacciones de fragmentos. Por ejemplo, a continuación, se incluye código que agrega un fragmento desde una clase Fragment existente:

Kotlin

val videoFragment = VideoPlayerFragment()
childFragmentManager.beginTransaction().apply {
    add(R.id.video_fragment, videoFragment)
    commit()
}

Java

Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();

Desde un fragmento anidado, puedes obtener una referencia al fragmento superior llamando a getParentFragment().

La biblioteca de compatibilidad de Android ahora también admite fragmentos anidados, por lo que puedes implementar diseños de fragmentos anidados en Android 1.6 y versiones posteriores.

Nota: No puedes aumentar un diseño hasta que se convierta en un fragmento cuando ese diseño incluye un <fragment>. Los fragmentos anidados solo se admiten cuando se agregan a un fragmento de forma dinámica.

RenderScript

Se mejoró la funcionalidad de procesamiento de Renderscript con las siguientes funciones:

Funciones intrínsecas de secuencias de comandos

Puedes usar las funciones intrínsecas de la secuencia de comandos integradas de Renderscript para implementar operaciones comunes, como las siguientes:

Para usar una secuencia de comandos intrínseca, llama al método estático create() de cada instrucción para crear una instancia de la secuencia de comandos. Luego, llama a los métodos set() disponibles de cada secuencia de comandos intrínseca para configurar las entradas y opciones necesarias. Por último, llama al método forEach() para ejecutar la secuencia de comandos.

Grupos de secuencias de comandos

Las ScriptGroup te permiten encadenar secuencias de comandos de RenderScript relacionadas y ejecutarlas con una sola llamada.

Usa un ScriptGroup.Builder para agregar todas las secuencias de comandos al grupo llamando a addKernel(). Una vez que agregues todas las secuencias de comandos, llama a addConnection() para crear las conexiones entre ellas. Cuando termines de agregar las conexiones, llama a create() para crear el grupo de secuencias de comandos. Antes de ejecutar el grupo de secuencias de comandos, especifica el Allocation de entrada y la secuencia de comandos inicial que se ejecutarán con el método setInput(Script.KernelID, Allocation) y proporciona el Allocation de salida en el que se escribirá el resultado y la secuencia de comandos final para que se ejecute con setOutput(). Por último, llama a execute() para ejecutar el grupo de secuencias de comandos.

FilterScript

Filterscript define restricciones en las APIs de Renderscript existentes que permiten que el código resultante se ejecute en una variedad más amplia de procesadores (CPU, GPU y DSP). Para crear archivos Filterscript, crea archivos .fs en lugar de archivos .rs y especifica #pragma rs_fp_relaxed para indicar al entorno de ejecución de Renderscript que tus secuencias de comandos no requieren una precisión estricta de punto flotante IEEE 754-2008. Esta precisión permite el vaciado a cero para denormales y el redondeo a cero. Además, las secuencias de comandos de Filterscript no deben usar tipos integrados de 32 bits y deben especificar una función raíz personalizada mediante el atributo __attribute__((kernel)), ya que Filterscript no admite punteros, que define la firma predeterminada de la función root().

Nota: Si bien la compatibilidad con Filterscript está en la plataforma, la asistencia para desarrolladores estará disponible en la versión 21.0.1 de las herramientas del SDK.

Para obtener una vista detallada de todos los cambios de las APIs de Android 4.2, consulta el Informe de diferencias de las APIs.