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ícitamenteandroid:exported="true"
.Este cambio solo se aplica si estableces
android:targetSdkVersion
oandroid: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 elACCESS_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 enSettings.System
que se movió aSettings.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
yandroid:minSdkVersion
es inferior a 17, la app no podrá modificar la configuración que se movió aSettings.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 tutargetSdkVersion
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 tuWebView
no podrá acceder al método cuando se ejecute en Android 4.2 o versiones posteriores. Si configurastargetSdkVersion
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 afinish()
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:
- Para acceder al almacenamiento interno, usa
getFilesDir()
,getCacheDir()
oopenFileOutput()
. - Para acceder al almacenamiento externo, usa
getExternalFilesDir()
ogetExternalStoragePublicDirectory()
.
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:
- 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 deandroid:layout_marginLeft
yandroid:layout_marginEnd
en lugar deandroid:layout_marginRight
.La clase
RelativeLayout
también proporciona los atributos de diseño correspondientes para reemplazar las posiciones izquierda/derecha, comoandroid:layout_alignParentStart
para reemplazarandroid:layout_alignParentLeft
yandroid:layout_toStartOf
en lugar deandroid:layout_toLeftOf
. - 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 enres/layout/
y tus diseños optimizados para RTL enres/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:
Blends
Blur
Color matrix
3x3 convolve
5x5 convolve
Per-channel lookup table
Converting an Android YUV buffer to RGB
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étodosset()
disponibles de cada secuencia de comandos intrínseca para establecer las entradas y opciones necesarias. Por último, llama al métodoforEach()
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 aaddKernel()
. Una vez que hayas agregado todas las secuencias de comandos, llama aaddConnection()
para crear las conexiones entre ellas. Cuando hayas terminado de agregar las conexiones, llama acreate()
para crear el grupo de secuencias de comandos. Antes de ejecutar el grupo de secuencias de comandos, especifica elAllocation
de entrada y la secuencia de comandos inicial para que se ejecuten con el métodosetInput(Script.KernelID, Allocation)
y proporciona laAllocation
de salida en la que se escribirá el resultado y la secuencia de comandos final para que se ejecute consetOutput()
. Por último, llama aexecute()
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ónroot()
.
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.