Livello API: 17
Android 4.2 (JELLY_BEAN_MR1
) è un aggiornamento della release di Jelly Bean che offre nuove funzionalità per utenti e sviluppatori di app. Questo documento offre un'introduzione alle nuove API più importanti
e utili per gli sviluppatori.
In qualità di sviluppatore di app, devi scaricare l'immagine di sistema Android 4.2 e la piattaforma SDK da SDK Manager il prima possibile. Se non hai un dispositivo con Android 4.2 su cui testare la tua app, utilizza l'immagine di sistema Android 4.2 per testare la tua app utilizzando l'emulatore Android. Poi crea le tue app sulla piattaforma Android 4.2 per iniziare a utilizzare le API più recenti.
Per ottimizzare meglio la tua app per i dispositivi con Android 4.2, devi impostare targetSdkVersion
su "17"
, installarla su un'immagine di sistema Android 4.2, testarla e pubblicare un aggiornamento con questa modifica.
Puoi
utilizzare le API in Android 4.2 e, al contempo, supportare le versioni precedenti, aggiungendo
condizioni al tuo codice che verificano il livello API di sistema prima di eseguire
API non supportate da minSdkVersion
.
Per scoprire di più sul mantenimento della compatibilità con le versioni precedenti, consulta la sezione Creare UI compatibili con le versioni precedenti.
Per ulteriori informazioni sul funzionamento dei livelli API, consulta Che cos'è il livello API?
Importanti modifiche del comportamento
Se hai già pubblicato un'app per Android, tieni presente le seguenti modifiche che potrebbero influire sul comportamento dell'app:
- I fornitori di contenuti non vengono più esportati per impostazione predefinita. In altre parole, il valore predefinito
dell'attributo
android:exported
è ora“false"
. Se è importante che altre app possano accedere al tuo fornitore di contenuti, ora devi impostare esplicitamenteandroid:exported="true"
.Questa modifica viene applicata solo se imposti
android:targetSdkVersion
oandroid:minSdkVersion
su 17 o su un valore superiore. In caso contrario, il valore predefinito è ancora“true"
anche su Android 4.2 e versioni successive. - Rispetto alle versioni precedenti di Android, i risultati relativi alla posizione dell'utente potrebbero essere meno precisi
se la tua app richiede l'autorizzazione
ACCESS_COARSE_LOCATION
ma non richiede l'autorizzazioneACCESS_FINE_LOCATION
.Per soddisfare le aspettative degli utenti in termini di privacy quando la tua app richiede l'autorizzazione per la posizione approssimativa (e non precisa), il sistema non fornirà una stima della posizione dell'utente più precisa di un isolato.
- Alcune impostazioni del dispositivo definite da
Settings.System
sono ora di sola lettura. Se la tua app tenta di scrivere modifiche alle impostazioni definite inSettings.System
che sono state spostate inSettings.Global
, l'operazione di scrittura non riuscirà automaticamente quando viene eseguita su Android 4.2 e versioni successive.Anche se il valore di
android:targetSdkVersion
eandroid:minSdkVersion
è inferiore a 17, la tua app non è in grado di modificare le impostazioni che sono state spostate inSettings.Global
se viene eseguita su Android 4.2 e versioni successive. - Se la tua app utilizza
WebView
, Android 4.2 aggiunge un ulteriore livello di sicurezza in modo che tu possa associare JavaScript al tuo codice Android in modo più sicuro. Se impostitargetSdkVersion
su 17 o su un valore superiore, ora devi aggiungere l'annotazione@JavascriptInterface
a qualsiasi metodo che vuoi rendere disponibile per il tuo codice JavaScript (anche questo metodo deve essere pubblico). Se non fornisci l'annotazione, il metodo non è accessibile da una pagina web nel tuoWebView
quando viene eseguito su Android 4.2 o versioni successive. Se impostitargetSdkVersion
su 16 o su un valore inferiore, l'annotazione non è obbligatoria, ma ti consigliamo di aggiornare la versione di destinazione e aggiungere l'annotazione per maggiore sicurezza.Scopri di più sull'associazione del codice JavaScript al codice Android.
Daydream
Daydream è una nuova modalità salvaschermo interattiva per dispositivi Android. Si attiva automaticamente quando il dispositivo viene inserito in un dock o quando il dispositivo viene lasciato inattivo mentre è collegato a un caricabatterie (anziché spegnere lo schermo). Daydream mostra un sogno alla volta, che può essere un display passivo puramente visivo che si chiude al tocco o essere interattivo e reattivo alla suite completa di eventi di input. I tuoi sogni vengono eseguiti nel processo dell'app e hanno accesso completo al toolkit dell'interfaccia utente di Android, inclusi visualizzazioni, layout e animazioni, quindi sono più flessibili e potenti degli sfondi animati o dei widget delle app.
Puoi creare un sogno per Daydream implementando una sottoclasse di DreamService
. Le API DreamService
sono
progettate per essere simili a quelle di Activity
. Per specificare la UI del tuo sogno, passa un ID risorsa di layout o View
a setContentView()
in qualsiasi momento dopo aver creato una finestra, ad esempio dal callback onAttachedToWindow()
.
La classe DreamService
fornisce altri metodi importanti di callback del ciclo di vita in aggiunta alle API Service
di base, come onDreamingStarted()
, onDreamingStopped()
e onDetachedFromWindow()
.
Non puoi avviare un DreamService
dalla tua
app, poiché viene avviato automaticamente dal sistema.
Se il tuo sogno è interattivo, puoi avviare un'attività da sogno per indirizzare l'utente nell'interfaccia utente completa della tua app per ottenere maggiori dettagli o controllo. Puoi utilizzare finish()
per terminare il sogno e consentire all'utente di vedere la nuova attività.
Per rendere disponibile daydream al sistema, dichiara DreamService
con un elemento <service>
nel file manifest. Devi quindi includere un filtro per intent con l'azione "android.service.dreams.DreamService"
. Ecco alcuni esempi:
<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>
In DreamService
esistono altri metodi utili da tenere a mente:
setInteractive(boolean)
controlla se il sogno riceve eventi di input o esce immediatamente dopo l'input dell'utente. Se il sogno è interattivo, l'utente può utilizzare i pulsanti Indietro o Home per uscire dal sogno oppure è possibile chiamarefinish()
per interromperlo.- Se vuoi un display completamente immersivo, puoi chiamare
setFullscreen()
per nascondere la barra di stato. - Prima dell'avvio di Daydream, la luminosità del display si abbassa per segnalare all'utente che il timeout di inattività si sta avvicinando. Se chiami il numero
setScreenBright(true)
, puoi invece impostare la luminosità normale del display.
Per saperne di più, consulta la documentazione di DreamService
.
Display secondari
Android ora consente alla tua app di mostrare contenuti esclusivi su schermate aggiuntive collegate al dispositivo dell'utente tramite una connessione cablata o Wi-Fi.
Per creare contenuti univoci per un display secondario, estendi la classe Presentation
e implementa il callback onCreate()
. All'interno di onCreate()
, specifica la UI per il display secondario chiamando setContentView()
.
Come estensione della classe Dialog
, la classe Presentation
fornisce l'area geografica in cui la tua app può mostrare un'interfaccia utente univoca sul
display secondario.
Per rilevare display secondari su cui puoi visualizzare Presentation
, utilizza l'API DisplayManager
o MediaRouter
. Sebbene le API DisplayManager
consentano di enumerare più visualizzazioni che possono essere collegate contemporaneamente, in genere è consigliabile utilizzare MediaRouter
per accedere rapidamente alla visualizzazione predefinita del sistema per le presentazioni.
Per visualizzare quella predefinita per la tua presentazione, chiama MediaRouter.getSelectedRoute()
e superala
ROUTE_TYPE_LIVE_VIDEO
. Viene restituito un oggetto MediaRouter.RouteInfo
che descrive la route attualmente selezionata dal sistema per le presentazioni video. Se MediaRouter.RouteInfo
non è null, chiama getPresentationDisplay()
per ottenere che Display
rappresenti il display connesso.
Puoi quindi visualizzare la presentazione passando l'oggetto Display
a un costruttore per la tua classe Presentation
. La presentazione verrà visualizzata
sul display secondario.
Per rilevare in fase di runtime quando è stato connesso un nuovo display, crea un'istanza di MediaRouter.SimpleCallback
in cui implementi il metodo di callback onRoutePresentationDisplayChanged()
, che il sistema chiamerà quando viene collegato un nuovo display di presentazione. Quindi registra MediaRouter.SimpleCallback
passandolo a MediaRouter.addCallback()
insieme al tipo di percorso ROUTE_TYPE_LIVE_VIDEO
. Quando ricevi una chiamata al numero onRoutePresentationDisplayChanged()
, basta chiamare il numero MediaRouter.getSelectedRoute()
come indicato sopra.
Per ottimizzare ulteriormente l'interfaccia utente di Presentation
per le
schermate secondarie, puoi applicare
un tema diverso specificando l'attributo android:presentationTheme
nella proprietà <style>
che hai
applicato alla tua applicazione o attività.
Tieni presente che gli schermi collegati al dispositivo dell'utente spesso hanno uno schermo più grande e probabilmente una densità diversa. Poiché le caratteristiche dello schermo possono variare, devi fornire risorse ottimizzate specificamente per schermi di dimensioni così grandi. Se devi richiedere risorse aggiuntive da Presentation
, chiama getContext()
.getResources()
per ottenere l'oggetto Resources
corrispondente al display. In questo modo vengono fornite le risorse dell'app appropriate per le dimensioni e la densità dello schermo del display secondario.
Per ulteriori informazioni e alcuni esempi di codice, consulta la documentazione della classe Presentation
.
Widget schermata di blocco
Android ora consente agli utenti di aggiungere widget di app alla schermata di blocco. Per poter utilizzare il widget per l'app nella schermata di blocco, aggiungi l'attributo android:widgetCategory
al file XML che specifica AppWidgetProviderInfo
. Questo attributo supporta due valori: home_screen
e keyguard
. Per impostazione predefinita, l'attributo è impostato su home_screen
, quindi gli utenti possono aggiungere
il widget dell'app alla schermata Home. Se vuoi che il widget dell'app sia disponibile anche nella schermata di blocco, aggiungi il valore keyguard
:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" ... android:widgetCategory="keyguard|home_screen"> </appwidget-provider>
Devi inoltre specificare un layout iniziale per il widget dell'app quando ti trovi nella schermata di blocco tramite
l'attributo android:initialKeyguardLayout
. Funziona come l'android:initialLayout
, in quanto fornisce un layout che può essere visualizzato immediatamente fino a quando il widget dell'app non viene inizializzato e non è in grado di aggiornarlo.
Per ulteriori informazioni sulla creazione di widget delle app per la schermata di blocco, incluso come regolare le dimensioni del widget quando è attiva la schermata di blocco, consulta la guida Widget app.
Più utenti
Android ora consente più spazi utente sui dispositivi condivisibili, come i tablet. Ogni utente di un dispositivo ha il proprio insieme di account, app, impostazioni di sistema, file e qualsiasi altro dato associato all'utente.
In qualità di sviluppatore di app, non devi fare nulla per far sì che l'app funzioni correttamente con più utenti su un singolo dispositivo. Indipendentemente dal numero di utenti che possono esistere su un dispositivo, i dati che la tua app salva per un determinato utente vengono mantenuti separati da quelli che l'app salva per altri utenti. Il sistema tiene traccia di quali dati utente appartengono al processo utente in cui è in esecuzione la tua app e fornisce alla tua app l'accesso solo ai dati di quell'utente e non consente l'accesso ai dati di altri utenti.
Salvataggio dei dati in un ambiente multiutente
Ogni volta che l'app salva le preferenze dell'utente, crea un database o scrive un file nello spazio di archiviazione interno o esterno dell'utente, i dati sono accessibili solo quando l'utente è in esecuzione.
Per assicurarti che la tua app funzioni correttamente in un ambiente multiutente, non fare riferimento alla directory interna dell'app o alla posizione di archiviazione esterna utilizzando percorsi hardcoded e utilizza sempre le API appropriate:
- Per accedere alla memoria interna, usa
getFilesDir()
,getCacheDir()
oopenFileOutput()
. - Per accedere alla memoria esterna, usa
getExternalFilesDir()
ogetExternalStoragePublicDirectory()
.
Indipendentemente dall'API utilizzata per salvare i dati di un determinato utente, i dati non saranno accessibili durante l'esecuzione come utente diverso. Dal punto di vista della tua app, ogni utente usa un dispositivo completamente separato.
Identificazione degli utenti in un ambiente multiutente
Se la tua app vuole identificare utenti unici, ad esempio per raccogliere dati di analisi o creare altre associazioni di account, segui le pratiche consigliate per identificare le installazioni univoche. Se crei un nuovo UUID
al primo avvio dell'app, hai la certezza di ottenere un ID univoco per il monitoraggio di ciascun utente, indipendentemente dal numero di utenti che installano l'app su un singolo dispositivo. In alternativa, puoi salvare un token locale recuperato dal tuo server o utilizzare l'ID di registrazione fornito da Google Cloud Messaging.
Tieni presente che se la tua app richiede uno degli identificatori del dispositivo hardware (ad esempio l'indirizzo MAC Wi-Fi o il numero SERIAL
), questi fornirà lo stesso valore per ogni utente perché questi identificatori sono associati all'hardware e non all'utente. Per non parlare degli altri problemi introdotti da questi identificatori, come discusso nel post del blog Identificazione delle installazioni di app.
Nuove impostazioni globali
Le impostazioni di sistema sono state aggiornate per supportare più utenti con l'aggiunta di Settings.Global
. Questa raccolta di impostazioni è simile a quella di Settings.Secure
perché sono di sola lettura, ma viene applicata a livello globale a tutti gli spazi utente del dispositivo.
Diverse impostazioni esistenti sono state spostate qui da Settings.System
o Settings.Secure
. Se al momento la tua app apporta modifiche a impostazioni precedentemente definite in Settings.System
(ad esempio AIRPLANE_MODE_ON
), ciò non funzionerà più sui dispositivi con Android 4.2 o versioni successive se tali impostazioni sono state spostate in Settings.Global
. Puoi continuare a leggere le impostazioni presenti in Settings.Global
. Tuttavia, poiché tali impostazioni non sono più considerate sicure per la modifica delle app, il tentativo di eseguire questa operazione non andrà a buon fine e il sistema scriverà un avviso nel log di sistema durante l'esecuzione dell'app su Android 4.2 o versioni successive.
Supporto del layout RTL
Android ora offre diverse API che ti consentono di creare interfacce utente che trasformano agevolmente l'orientamento del layout per supportare le lingue che utilizzano UI e indicazioni di lettura con scrittura da destra a sinistra (RTL), come l'arabo e l'ebraico.
Per iniziare a supportare i layout RTL nella tua app, imposta l'attributo android:supportsRtl
sull'elemento <application>
nel file manifest
e impostalo su “true"
. Una volta abilitata, il sistema abiliterà varie API RTL per visualizzare la tua app con layout RTL. Ad esempio, la barra delle azioni mostrerà l'icona e il titolo sul lato destro e i pulsanti di azione a sinistra; inoltre, verranno invertiti anche tutti i layout che hai creato con le classi View
fornite dal frame.
Se hai bisogno di ottimizzare ulteriormente l'aspetto della tua app quando viene visualizzata con un layout RTL, esistono due livelli di base di ottimizzazione:
- Convertire le proprietà di layout orientate a sinistra e a destra in proprietà di layout con orientamento iniziale e finale.
Ad esempio, utilizza
android:layout_marginStart
al posto diandroid:layout_marginLeft
eandroid:layout_marginEnd
al posto diandroid:layout_marginRight
.La classe
RelativeLayout
fornisce anche gli attributi di layout corrispondenti per sostituire le posizioni a sinistra/destra, ad esempioandroid:layout_alignParentStart
per sostituireandroid:layout_alignParentLeft
eandroid:layout_toStartOf
anzichéandroid:layout_toLeftOf
. - In alternativa, per un'ottimizzazione completa dei layout RTL, puoi fornire file di layout completamente separati utilizzando il qualificatore delle risorse
ldrtl
(ldrtl
sta per layout-direction-right-to-left}). Ad esempio, puoi salvare i file di layout predefiniti inres/layout/
e i layout ottimizzati RTL inres/layout-ldrtl/
.Il qualificatore
ldrtl
è ottimo per le risorse disegnabili, in modo da poter fornire grafici orientati nella direzione corrispondente a quella di lettura.
Nel framework sono disponibili altre API per supportare i layout RTL, ad esempio nella classe View
per implementare i comportamenti appropriati per le viste personalizzate e in Configuration
per eseguire query sulla direzione del layout corrente.
Nota: se utilizzi SQlite e i nomi delle tabelle o delle colonne sono "solo numeri", fai attenzione: l'utilizzo di String.format(String, Object...)
può causare errori in cui i numeri sono stati convertiti nei loro equivalenti arabi se sul tuo dispositivo sono state impostate le impostazioni internazionali in arabo.
Devi utilizzare String.format(Locale,String,Object...)
per assicurarti che i numeri vengano
conservati come ASCII. Utilizza anche String.format("%d", int)
anziché String.valueOf(int)
per la formattazione dei numeri.
Frammenti nidificati
Ora puoi incorporare frammenti all'interno di essi. Questo è utile per una varietà di situazioni in cui vuoi inserire componenti dinamici e riutilizzabili dell'interfaccia utente in un componente dell'interfaccia utente che è a sua volta dinamico e riutilizzabile. Ad esempio, se utilizzi ViewPager
per creare frammenti che scorrono verso sinistra e destra e occupano la maggior parte dello spazio sullo schermo, ora puoi inserire frammenti nella pagina di ogni frammento.
Per nidificare un frammento, è sufficiente chiamare getChildFragmentManager()
sul Fragment
in cui vuoi aggiungere un frammento. Questo restituisce un valore FragmentManager
che puoi utilizzare come fai normalmente dall'attività di primo livello
per creare transazioni con frammenti. Ad esempio, ecco del codice che aggiunge un frammento dall'interno di una classe Fragment
esistente:
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();
Da un frammento nidificato, puoi ottenere un riferimento al frammento padre chiamando getParentFragment()
.
Android Support Library ora supporta anche i frammenti nidificati, quindi puoi implementare progettazioni di frammenti nidificate su Android 1.6 e versioni successive.
Nota: non puoi gonfiare un layout in un frammento se questo include un <fragment>
. I frammenti nidificati sono supportati solo se aggiunti a un frammento in modo dinamico.
RenderScript
La funzionalità di calcolo di Renderscript è stata migliorata con le seguenti caratteristiche:
- Caratteristiche intrinseche dello script
Puoi utilizzare le funzionalità intrinseche degli script integrate di Renderscript che implementano operazioni comuni per te, ad esempio:
Blends
Blur
Color matrix
3x3 convolve
5x5 convolve
Per-channel lookup table
Converting an Android YUV buffer to RGB
Per utilizzare uno script intrinseco, chiama il metodo
create()
statico di ogni instrinsic per creare un'istanza dello script. Quindi, chiami i metodiset()
disponibili di ogni script intrinseci per impostare gli input e le opzioni necessari. Infine, chiama il metodoforEach()
per eseguire lo script.- Gruppi di script
-
Gli
ScriptGroup
consentono di concatenare script Renderscript correlati ed eseguirli con un'unica chiamata.Utilizza un
ScriptGroup.Builder
per aggiungere tutti gli script al gruppo chiamando il numeroaddKernel()
. Dopo aver aggiunto tutti gli script, crea le connessioni tra gli script chiamandoaddConnection()
. Quando hai finito di aggiungere le connessioni, chiamacreate()
per creare il gruppo di script. Prima di eseguire il gruppo di script, specifica l'inputAllocation
e lo script iniziale da eseguire con il metodosetInput(Script.KernelID, Allocation)
e fornisci l'outputAllocation
in cui verrà scritto il risultato e lo script finale da eseguire consetOutput()
. Infine, chiamaexecute()
per eseguire il gruppo di script. - Script filtro
-
Filtroscript definisce i vincoli sulle API Renderscript esistenti che consentono l'esecuzione del codice risultante su una gamma più ampia di processori (CPU, GPU e DSP). Per creare file Filterscript, crea file
.fs
anziché.rs
e specifica#pragma rs_fp_relaxed
per comunicare al runtime di Renderscript i tuoi script non richiedono una precisione in virgola mobile rigorosa IEEE 754-2008. Questa precisione consente lo svuotamento a zero per i denormi e l'arrotondamento verso zero. Inoltre, gli script di filtro non devono utilizzare tipi integrati a 32 bit e devono specificare una funzione radice personalizzata utilizzando l'attributo__attribute__((kernel))
, poiché il filtro non supporta i puntatori, che sono definiti dalla firma predefinita della funzioneroot()
.
Nota: sebbene il supporto di Filterscript sia nella piattaforma, l'assistenza per gli sviluppatori sarà disponibile nella versione 21.0.1 di SDK Tools.
Per una visualizzazione dettagliata di tutte le modifiche alle API in Android 4.2, consulta il report Differenze API.