I processi in background possono richiedere molta memoria e batteria. Ad esempio, una trasmissione implicita potrebbe avviare molti processi in background che si sono registrati per ascoltarla, anche se questi processi potrebbero non fare molto lavoro. Questo può avere un impatto significativo sia sulle prestazioni del dispositivo sia sull'esperienza utente.
Per ovviare a questo problema, Android 7.0 (livello API 24) applica quanto segue: restrizioni:
- Le app che hanno come target Android 7.0 (livello API 24) e versioni successive non ricevono broadcasting
CONNECTIVITY_ACTION
se dichiarano il proprio ricevitore di trasmissione nel file manifest. Le app continueranno a ricevere le trasmissioniCONNECTIVITY_ACTION
se registrano il proprioBroadcastReceiver
conContext.registerReceiver()
e il contesto è ancora valido. - Le app non possono inviare o ricevere trasmissioni
ACTION_NEW_PICTURE
oACTION_NEW_VIDEO
. Questa ottimizzazione riguarda tutte le app, non solo quelle destinate ad Android 7.0 (livello API 24).
Se la tua app utilizza uno di questi intent, devi rimuovere le dipendenze
il prima possibile per poter scegliere correttamente come target i dispositivi con Android 7.0
o superiore. Il framework Android offre diverse soluzioni per mitigare
necessarie per queste trasmissioni implicite. Ad esempio, JobScheduler
e il nuovo WorkManager forniscono meccanismi solidi per pianificare le operazioni di rete quando vengono soddisfatte condizioni specifiche, ad esempio una connessione a una rete senza misurazione. Ora puoi anche usare JobScheduler
per reagire alle modifiche
ai fornitori di contenuti. JobInfo
gli oggetti incapsulano i parametri JobScheduler
per pianificare il job. Quando le condizioni del job sono soddisfatte, il sistema lo esegue sul JobService
della tua app.
In questa pagina impareremo a utilizzare metodi alternativi, come
JobScheduler
, per adattare la tua app a queste nuove
limitazioni.
Restrizioni avviate dall'utente
Nella pagina Utilizzo batteria delle impostazioni del sistema, l'utente può scegliere tra le seguenti opzioni:
- Senza restrizioni: consenti tutte le attività in background, il che potrebbe comportare un maggiore consumo della batteria.
- Ottimizzata (predefinita): ottimizza la capacità di un'app di eseguire attività in background in base al modo in cui l'utente interagisce con l'app.
- Con restrizioni: impedisce completamente a un'app di essere eseguita in background. App potrebbe non funzionare come previsto.
Se un'app presenta alcuni dei comportamenti dannosi descritti in Android vitals, il sistema potrebbe chiedere all'utente di limitare l'accesso dell'app alle risorse di sistema.
Se il sistema rileva che un'app sta consumando risorse eccessive, lo comunica all'utente e gli offre la possibilità di limitare le azioni dell'app. I comportamenti che possono attivare la notifica includono:
- Wakelock eccessivi: 1 wakelock parziale mantenuto per un'ora quando lo schermo è spento
- Servizi in background eccessivi: se l'app ha come target livelli API inferiori a 26 e ha servizi in background eccessivi
Le limitazioni precise imposte sono determinate dal produttore del dispositivo. Per Ad esempio, sulle build AOSP con Android 9 (livello API 28) o versioni successive, le app in esecuzione in background che si trovano nella zona "limitata" hanno le seguenti caratteristiche: limitazioni:
- Impossibile avviare i servizi in primo piano
- I servizi in primo piano esistenti vengono rimossi dal primo piano
- Le sveglie non vengono attivate
- I job non vengono eseguiti
Inoltre, se un'app ha come target Android 13 (livello API 33) o versioni successive ed è nello stato "con restrizioni", il sistema non pubblica la trasmissione BOOT_COMPLETED
o la trasmissione LOCKED_BOOT_COMPLETED
finché l'app non viene avviata per altri motivi.
Le restrizioni specifiche sono elencate in Restrizioni per la gestione dell'alimentazione.
Limitazioni alla ricezione delle trasmissioni delle attività di rete
Le app che hanno come target Android 7.0 (livello API 24) non ricevono annunci da CONNECTIVITY_ACTION
se
registrarsi per riceverle nel file manifest, e i processi che dipendono da questo
la trasmissione non verrà avviata. Ciò potrebbe rappresentare un problema per le app che vogliono monitorare le modifiche alla rete o eseguire attività di rete collettive quando il dispositivo si connette a una rete senza limiti di traffico. Esistono già diverse soluzioni per aggirare questa limitazione nel framework Android, ma la scelta della soluzione giusta dipende da ciò che vuoi che la tua app realizzi.
Nota: un BroadcastReceiver
registrato con
Context.registerReceiver()
continua a ricevere questi annunci mentre l'app è in esecuzione.
Pianificare i job di rete su connessioni non conteggiate
Quando utilizzi la classe JobInfo.Builder
per creare l'oggetto JobInfo
, applica il metodo setRequiredNetworkType()
e passa JobInfo.NETWORK_TYPE_UNMETERED
come parametro del job. Il seguente esempio di codice
pianifica l'esecuzione di un servizio quando il dispositivo si connette a un
rete e in carica:
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MyJobService::class.java) ) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build() jobScheduler.schedule(job) }
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo job = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MyJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) .setRequiresCharging(true) .build(); js.schedule(job); }
Quando le condizioni per il job sono soddisfatte, l'app riceve un callback per eseguire il metodo onStartJob()
nel JobService.class
specificato. Per vedere altri esempi di implementazione di JobScheduler
, consulta l'app di esempio JobScheduler.
Una nuova alternativa a JobScheduler è WorkManager, un'API che consente di pianificare le attività in background che richiedono il completamento garantito, indipendentemente dal fatto che il processo dell'app sia attivo o meno. WorkManager sceglie il modo appropriato per eseguire il lavoro (direttamente su un thread nel processo dell'app, nonché utilizzando JobScheduler, FirebaseJobDispatcher o AlarmManager) in base a fattori come il livello dell'API del dispositivo. Inoltre, WorkManager non richiede Play Services e fornisce diverse funzionalità avanzate, come il concatenamento delle attività o il controllo dello stato di un'attività. Per scoprire di più, consulta WorkManager.
Monitorare la connettività di rete durante l'esecuzione dell'app
Le app in esecuzione possono comunque ascoltare CONNECTIVITY_CHANGE
con un BroadcastReceiver
registrato. Tuttavia, l'API ConnectivityManager
fornisce un metodo più affidabile per richiedere un callback solo quando sono soddisfatte condizioni di rete specifiche.
Gli oggetti NetworkRequest
definiscono i parametri
di callback di rete in termini di NetworkCapabilities
. Hai creato oggetti NetworkRequest
con la classe NetworkRequest.Builder
. registerNetworkCallback()
poi passa l'oggetto NetworkRequest
al sistema. Quando
se le condizioni di rete sono soddisfatte, l'app riceve un callback per eseguire
Metodo onAvailable()
definito nella relativa classe ConnectivityManager.NetworkCallback
.
L'app continua a ricevere callback finché non esce o non chiama
unregisterNetworkCallback()
.
Limitazioni relative alla ricezione di trasmissioni di immagini e video
In Android 7.0 (livello API 24), le app non possono inviare o ricevere annunci ACTION_NEW_PICTURE
o ACTION_NEW_VIDEO
. Questa restrizione consente
ridurre gli impatti sulle prestazioni e sull'esperienza utente quando più app devono
per elaborare una nuova immagine o un nuovo video. Android 7.0 (livello API 24)
estende JobInfo
e JobParameters
per fornire una soluzione alternativa.
Attivare i job in base alle modifiche agli URI dei contenuti
Per attivare i job in base alle modifiche degli URI dei contenuti, Android 7.0 (livello API 24) estende
l'API JobInfo
con i seguenti metodi:
-
JobInfo.TriggerContentUri()
- Incapsula i parametri necessari per attivare un job in caso di modifiche all'URI dei contenuti.
-
JobInfo.Builder.addTriggerContentUri()
-
Passa un oggetto
TriggerContentUri
aJobInfo
. UnContentObserver
monitora l'URI dei contenuti incapsulati. Se a un job sono associati più oggettiTriggerContentUri
, il sistema fornisce un callback anche se segnala una modifica in uno solo degli URI dei contenuti. -
Aggiungi il flag
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
per attivare il job se uno dei discendenti dell'URI specificato cambia. Questo flag corrisponde al parametronotifyForDescendants
passato aregisterContentObserver()
.
Nota: TriggerContentUri()
non può essere utilizzato in
in combinazione con setPeriodic()
o setPersisted()
. Per monitorare continuamente le modifiche ai contenuti, pianifica un nuovo
JobInfo
prima che il JobService
dell'app finisca di gestire il richiamata più recente.
Il seguente codice di esempio pianifica un job da attivare quando il sistema segnala una modifica all'URI dei contenuti, MEDIA_URI
:
const val MY_BACKGROUND_JOB = 0 ... fun scheduleJob(context: Context) { val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = JobInfo.Builder( MY_BACKGROUND_JOB, ComponentName(context, MediaContentJob::class.java) ) .addTriggerContentUri( JobInfo.TriggerContentUri( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS ) ) .build() jobScheduler.schedule(job) }
public static final int MY_BACKGROUND_JOB = 0; ... public static void scheduleJob(Context context) { JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo.Builder builder = new JobInfo.Builder( MY_BACKGROUND_JOB, new ComponentName(context, MediaContentJob.class)); builder.addTriggerContentUri( new JobInfo.TriggerContentUri(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)); js.schedule(builder.build()); }
Quando il sistema segnala una modifica negli URI dei contenuti specificati, la tua app
riceve un callback e un oggetto JobParameters
viene
passato al metodo onStartJob()
in MediaContentJob.class
.
Determinare quali autorità sui contenuti hanno attivato un job
Android 7.0 (livello API 24) estende inoltre JobParameters
per consentire alla tua app di ricevere informazioni utili su quali autorità dei contenuti e URI hanno attivato il job:
-
Uri[] getTriggeredContentUris()
-
Restituisce un array di URI che hanno attivato il job. Il valore sarà
null
se nessun URI ha attivato il job (ad esempio, il job è attivato per una scadenza o per altri motivi) oppure il numero di modifiche Il numero di URI è maggiore di 50. -
String[] getTriggeredContentAuthorities()
-
Restituisce un array di stringhe di autorità sui contenuti che hanno attivato il job.
Se l'array restituito non è
null
, utilizzagetTriggeredContentUris()
per recuperare i dettagli degli URI che sono stati modificati.
Il seguente codice di esempio sostituisce il metodo JobService.onStartJob()
e registra le autorità dei contenuti e gli URI che hanno attivato il job:
override fun onStartJob(params: JobParameters): Boolean { StringBuilder().apply { append("Media content has changed:\n") params.triggeredContentAuthorities?.also { authorities -> append("Authorities: ${authorities.joinToString(", ")}\n") append(params.triggeredContentUris?.joinToString("\n")) } ?: append("(No content)") Log.i(TAG, toString()) } return true }
@Override public boolean onStartJob(JobParameters params) { StringBuilder sb = new StringBuilder(); sb.append("Media content has changed:\n"); if (params.getTriggeredContentAuthorities() != null) { sb.append("Authorities: "); boolean first = true; for (String auth : params.getTriggeredContentAuthorities()) { if (first) { first = false; } else { sb.append(", "); } sb.append(auth); } if (params.getTriggeredContentUris() != null) { for (Uri uri : params.getTriggeredContentUris()) { sb.append("\n"); sb.append(uri); } } } else { sb.append("(No content)"); } Log.i(TAG, sb.toString()); return true; }
Ottimizza ulteriormente l'app
Ottimizzare le app in modo che vengano eseguite su dispositivi con memoria ridotta o scarsa possono migliorare le prestazioni e l'esperienza utente. La rimozione delle dipendenze dai servizi in background e dai broadcast receiver impliciti registrati nel manifest può contribuire a migliorare il funzionamento della tua app su questi dispositivi. Sebbene Android 7.0 (livello API 24) adotta misure per ridurre alcuni di questi problemi, ti consigliamo di ottimizzare l'app in modo che venga eseguita senza utilizzare questi processi in background completamente.
I seguenti comandi Android Debug Bridge (ADB) possono aiutarti a testare il comportamento dell'app con le procedure in background disattivate:
- Per simulare condizioni in cui trasmissioni implicite e servizi in background non sono disponibili, inserisci il seguente comando:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
- Per riattivare le trasmissioni implicite e i servizi in background, inserisci il seguente comando:
-
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
- Puoi simulare l'inserimento dell'app nell'elenco "limitato" stato per l'utilizzo della batteria in background. Questa impostazione impedisce all'app di essere eseguita in background. Per farlo, esegui il seguente comando in una finestra del terminale:
-
$ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny