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 sobre las nuevas APIs 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 puedas probar la app, usa la imagen del sistema de Android 4.2 para probarla en el emulador de Android. 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 dispositivos que ejecutan Android 4.2, debes configurar targetSdkVersion como "17", instalarla en una imagen del sistema de Android 4.2, probarla y, luego, publicar una actualización con este cambio.

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

Para obtener más información sobre el funcionamiento de los niveles de API, consulta ¿Qué es el 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 su comportamiento:

  • Los proveedores de contenido ya no se exportan de forma predeterminada. Es decir, el valor predeterminado para el 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 de ubicación aproximada (y no precisa), el sistema no proporcionará una estimación de la ubicación del usuario que sea más precisa que una manzana.

  • Algunos de los 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 movió 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 la configuración que se movió 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 en tu WebView no podrá acceder al método cuando se ejecute en Android 4.2 o versiones posteriores. Si configuras targetSdkVersion en 16 o en una versión anterior, 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 cómo vincular el código JavaScript al 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). El protector de pantalla interactivo muestra un sueño a la vez, que puede ser una pantalla pasiva y puramente visual que descarta la imagen cuando se toca, o puede ser interactiva y receptiva a todo el conjunto 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 las vistas, los diseños y las animaciones, por lo que son más flexibles y potentes que los fondos 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 tengas 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 una DreamService desde tu app; el sistema la inicia automáticamente.

Si tu sueño es interactivo, puedes iniciar una actividad desde el sueño para enviar al usuario a la IU completa de tu app para 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 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 bien 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 establecer 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 puedas mostrar tu Presentation, usa las APIs de DisplayManager o MediaRouter. Si bien las APIs de DisplayManager te permiten enumerar varias pantallas que se pueden conectar a la vez, generalmente 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 de video. Si el 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 cuándo se conectó una pantalla nueva, 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 de 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 diferir, debes proporcionar recursos que estén optimizados específicamente para pantallas de este tipo más 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 de bloqueo

Android ahora permite a los usuarios agregar widgets de apps a la pantalla de bloqueo. Si deseas 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 de inmediato hasta que se inicialice el widget de la app y 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 en 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. Independientemente de 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 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 otorga acceso a los datos de ese usuario únicamente y no permite el acceso a los datos de otros usuarios.

Guardar 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 ejecuta como ese usuario.

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

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

Identifica usuarios en un entorno multiusuario

Si tu app quiere identificar usuarios únicos, por ejemplo, para recopilar estadísticas o crear otras asociaciones de cuentas, debes seguir las prácticas recomendadas para identificar instalaciones únicas. Si creas un objeto UUID nuevo cuando tu app se inicie por primera vez, tendrás la seguridad de obtener un ID único para realizar el seguimiento de cada usuario, independientemente de la cantidad de usuarios que la instalen 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

Se agregó Settings.Global para actualizar la configuración del sistema para admitir varios usuarios. Este conjunto de parámetros de configuración es similar a la configuración de Settings.Secure, ya que es de solo lectura, pero se aplica globalmente en todos los espacios de usuario del dispositivo.

Varios parámetros de configuración existentes se trasladaron aquí desde Settings.System o Settings.Secure. Si tu app realiza cambios en la configuración que se definió anteriormente en Settings.System (como AIRPLANE_MODE_ON), deberías esperar que esta acción ya no funcione en un dispositivo con Android 4.2 o versiones posteriores si esos parámetros se movieron a Settings.Global. Puedes seguir leyendo la configuración que se encuentra en Settings.Global, pero como ya no se considera que la configuración es segura para que las apps cambien, el intento hacerlo fallará de forma silenciosa y el sistema escribirá una advertencia en el registro del sistema cuando ejecutes tu app en Android 4.2 o versiones posteriores.

Compatibilidad con diseño de derecha a izquierda

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 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, establece el atributo android:supportsRtl en el elemento <application> de tu archivo de manifiesto y establécelo como “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, existen 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/derecha, como android:layout_alignParentStart para reemplazar android:layout_alignParentLeft y android:layout_toStartOf en lugar de android:layout_toLeftOf.

  2. Como alternativa, para proporcionar una optimización completa para diseños de derecha a izquierda, puedes proporcionar archivos de diseño completamente separados por medio del calificador de recursos ldrtl (ldrtl significa dirección del diseño de derecha a izquierda}). Por ejemplo, puedes guardar tus archivos de diseño predeterminados en res/layout/ y tus 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 se orienten en la dirección correspondiente a la dirección de lectura.

Hay varias otras APIs disponibles en el framework que admiten 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 del diseño actual.

Nota: Si usas SQlite y tienes nombres de columnas o tablas que 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á establecido en la configuración regional en árabe. Debes usar String.format(Locale,String,Object...) para asegurarte de que los números se conserven como códigos ASCII. También usa String.format("%d", int) en lugar de usar String.valueOf(int) para darles formato a los números.

Fragmentos anidados

Ahora puedes incorporar fragmentos dentro de fragmentos. Esto es útil para una variedad de situaciones en las que deseas colocar componentes de la IU dinámicos y reutilizables en un componente de la IU que en sí mismo sea dinámico y reutilizable. Por ejemplo, si usas ViewPager para crear fragmentos que se deslizan hacia la izquierda y la derecha, y consumen 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 en el que quieras agregar un fragmento. Se mostrará 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, te mostramos parte de 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 el interior de 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 y convertirlo 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 características:

Funciones intrínsecas de secuencias de comandos

Puedes usar las funciones intrínsecas de 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 establecer 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 llamada.

Usa un ScriptGroup.Builder para agregar todas las secuencias de comandos al grupo llamando a addKernel(). Una vez que hayas agregado todas las secuencias de comandos, llama a addConnection() para crear las conexiones entre ellas. Cuando hayas terminado 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 para que se ejecuten con el método setInput(Script.KernelID, Allocation) y proporciona la Allocation de salida en la 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 de punto flotante IEEE 754-2008 estricta. 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 con 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 de 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 la API en Android 4.2, consulta el Informe de diferencias de las APIs.