I processi in background possono consumare 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 svolgere molto lavoro. Ciò può avere un impatto sostanziale sia sulle prestazioni del dispositivo sia sull'esperienza utente.
Per ovviare a questo problema, Android 7.0 (livello API 24) applica le seguenti limitazioni:
- Le app che hanno come target Android 7.0 (livello API 24) e versioni successive non ricevono
trasmissioni
CONNECTIVITY_ACTIONse dichiarano il broadcast receiver nel manifest. Le app continueranno a ricevere i broadcastCONNECTIVITY_ACTIONse registrano i propriBroadcastReceiverconContext.registerReceiver()e se il contesto è ancora valido. - Le app non possono inviare o ricevere trasmissioni
ACTION_NEW_PICTUREoACTION_NEW_VIDEO. Questa ottimizzazione interessa tutte le app, non solo quelle che hanno come target 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 versioni successive. Il framework Android fornisce diverse soluzioni per ridurre la necessità di queste trasmissioni implicite. Ad esempio, JobScheduler e il nuovo WorkManager forniscono meccanismi efficaci per pianificare le operazioni di rete quando vengono soddisfatte condizioni specifiche, come una connessione a una rete non a consumo. Ora puoi anche utilizzare JobScheduler
per reagire alle modifiche ai provider di contenuti. Gli oggetti JobInfo incapsulano i parametri utilizzati da JobScheduler per pianificare il job. Quando le condizioni del job sono soddisfatte, il sistema esegue questo job sul JobService della tua app.
In questa pagina impareremo a utilizzare metodi alternativi, come JobScheduler, per adattare la tua app a queste nuove limitazioni.
Limitazioni avviate dall'utente
Nella pagina Utilizzo della batteria all'interno delle impostazioni di sistema, l'utente può scegliere tra le seguenti opzioni:
- Senza limitazioni:consente tutte le attività in background, il che potrebbe consumare più batteria.
- Ottimizzato (impostazione predefinita): ottimizza la capacità di un'app di eseguire operazioni in background, in base al modo in cui l'utente interagisce con l'app.
- Con limitazioni:impedisce completamente l'esecuzione di un'app in background. Le app potrebbero non funzionare come previsto.
Se un'app mostra alcune delle prestazioni scadenti descritte 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, invia una notifica all'utente e gli offre la possibilità di limitare le azioni dell'app. I comportamenti che possono attivare l'avviso includono:
- Wakelock eccessivi: 1 wakelock parziale attivo 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. Ad esempio, nelle build AOSP con Android 9 (livello API 28) o versioni successive, le app in esecuzione in background che si trovano nello stato "con limitazioni" presentano le seguenti limitazioni:
- Impossibile avviare i servizi in primo piano
- I servizi in primo piano esistenti vengono rimossi dal primo piano
- Gli allarmi non vengono attivati
- I job non vengono eseguiti
Inoltre, se un'app ha come target Android 13 (livello API 33) o versioni successive ed è in stato
"con limitazioni", il sistema non invia la trasmissione BOOT_COMPLETED o
la trasmissione LOCKED_BOOT_COMPLETED finché l'app non viene avviata per altri
motivi.
Le limitazioni specifiche sono elencate in Limitazioni per la gestione dell'alimentazione.
Limitazioni alla ricezione di trasmissioni di attività di rete
Le app che hanno come target Android 7.0 (livello API 24) non ricevono trasmissioni CONNECTIVITY_ACTION se si registrano per riceverle nel manifest e i processi che dipendono da questa trasmissione non verranno avviati. Questo potrebbe rappresentare un problema per le app che vogliono ascoltare le modifiche alla rete o eseguire attività di rete collettive quando il dispositivo si connette a una rete non a consumo. Nel framework Android esistono già diverse soluzioni per aggirare questa limitazione, ma la scelta di quella giusta dipende da ciò che vuoi che la tua app faccia.
Nota: un BroadcastReceiver registrato con
Context.registerReceiver()
continua a ricevere queste trasmissioni mentre l'app è in esecuzione.
Pianifica i job di rete su connessioni non a consumo
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 una rete non a consumo ed è in carica:
Kotlin
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) }
Java
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 altri esempi di implementazione di JobScheduler, consulta l'app di esempio JobScheduler.
Una nuova alternativa a JobScheduler è WorkManager, un'API che ti consente di pianificare attività in background che richiedono il completamento garantito, indipendentemente dal fatto che il processo dell'app sia in esecuzione o meno. WorkManager sceglie il modo appropriato per eseguire il lavoro (direttamente su un thread nel processo dell'app o utilizzando JobScheduler, FirebaseJobDispatcher o AlarmManager) in base a fattori come il livello API del dispositivo. Inoltre, WorkManager non richiede Play Services e offre diverse funzionalità avanzate, come il concatenamento delle attività o il controllo dello stato di un'attività. Per saperne di più, consulta WorkManager.
Monitorare la connettività di rete mentre l'app è in esecuzione
Le app in esecuzione possono comunque ascoltare CONNECTIVITY_CHANGE con un BroadcastReceiver registrato. Tuttavia, l'API ConnectivityManager fornisce un metodo più efficace per richiedere un callback solo quando vengono soddisfatte determinate condizioni di rete.
Gli oggetti NetworkRequest definiscono i parametri del callback di rete in termini di NetworkCapabilities. Puoi creare oggetti NetworkRequest con la classe NetworkRequest.Builder. registerNetworkCallback()
passa quindi l'oggetto NetworkRequest al sistema. Quando
le condizioni della rete sono soddisfatte, l'app riceve un callback per eseguire il
metodo onAvailable() definito nella classe ConnectivityManager.NetworkCallback.
L'app continua a ricevere callback finché non viene chiusa o non chiama unregisterNetworkCallback().
Limitazioni alla ricezione di trasmissioni di immagini e video
In Android 7.0 (livello API 24), le app non possono inviare o ricevere trasmissioni ACTION_NEW_PICTURE o ACTION_NEW_VIDEO. Questa limitazione contribuisce ad attenuare l'impatto sulle prestazioni e sull'esperienza utente quando più app devono attivarsi per elaborare una nuova immagine o un nuovo video. Android 7.0 (livello API 24)
estende JobInfo e JobParameters per fornire una soluzione alternativa.
Attiva i job in base alle modifiche dell'URI contenuto
Per attivare i job in caso di modifiche all'URI contenuto, 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
TriggerContentUriaJobInfo. UnContentObservermonitora l'URI contenuto incapsulato. Se a un job sono associati più oggettiTriggerContentUri, il sistema fornisce un callback anche se segnala una modifica in uno solo degli URI contenuto. -
Aggiungi il flag
TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTSper attivare il job se i discendenti dell'URI specificato cambiano. Questo flag corrisponde al parametronotifyForDescendantspassato aregisterContentObserver().
Nota: TriggerContentUri() non può essere utilizzato in combinazione con setPeriodic() o setPersisted(). Per monitorare continuamente le modifiche ai contenuti, pianifica un nuovo JobInfo prima che JobService dell'app termini di gestire il callback più recente.
Il seguente codice campione pianifica un job da attivare quando il sistema segnala una modifica all'URI contenuto, MEDIA_URI:
Kotlin
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) }
Java
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à dei contenuti hanno attivato un job
Android 7.0 (livello API 24) estende anche 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. Questo valore sarà
nullse nessun URI ha attivato il job (ad esempio, il job è stato attivato a causa di una scadenza o per un altro motivo) o se il numero di URI modificati è maggiore di 50. -
String[] getTriggeredContentAuthorities() -
Restituisce un array di stringhe di autorità dei 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 campione esegue l'override del metodo JobService.onStartJob() e registra le autorità dei contenuti e gli URI che hanno attivato il job:
Kotlin
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 }
Java
@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 la tua app
L'ottimizzazione delle app per l'esecuzione su dispositivi con memoria ridotta o in condizioni di memoria ridotta può 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) adotti misure per ridurre alcuni di questi problemi, ti consigliamo di ottimizzare la tua app in modo che venga eseguita senza l'utilizzo di questi processi in background.
I seguenti comandi Android Debug Bridge (ADB) possono aiutarti a testare il comportamento dell'app con i processi in background disattivati:
- Per simulare condizioni in cui le trasmissioni implicite e i 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'utente che imposta la tua app sullo stato "con limitazioni" per l'utilizzo della batteria in background. Questa impostazione impedisce l'esecuzione dell'app in background. Per farlo, esegui questo comando in una finestra del terminale:
-
$ adb shell cmd appops set <PACKAGE_NAME> RUN_ANY_IN_BACKGROUND deny