Para evitar o descarregamento da bateria, um dispositivo Android entra em estado de suspensão rapidamente quando fica ocioso. No entanto, há momentos em que um app precisa ativar a tela ou a CPU e mantê-la ativa para concluir algum trabalho.
O método escolhido depende das necessidades do app. No entanto, uma regra geral é usar a abordagem mais leve possível para o app, a fim de minimizar o impacto nos recursos do sistema. As seções a seguir descrevem como lidar com os casos em que o comportamento de suspensão padrão do dispositivo é incompatível com os requisitos do app.
Alternativas ao uso de wake locks
Antes de oferecer o recurso de wake lock, considere se os casos de uso do seu app são compatíveis com uma das seguintes soluções alternativas:
- Se o app estiver executando downloads HTTP de longa duração, avalie a possibilidade de usar
DownloadManager
. - Se o app estiver sincronizando dados de um servidor externo, considere criar um adaptador de sincronização.
- Se o app depende de serviços em segundo plano, use o JobScheduler ou o Firebase Cloud Messaging para acionar esses serviços em intervalos específicos.
Manter a tela ativada
Alguns apps precisam manter a tela ativada, por exemplo, os apps de jogos ou filmes. A melhor maneira de fazer isso é usar FLAG_KEEP_SCREEN_ON
na sua atividade e apenas em uma atividade, nunca em um serviço ou outro componente de app. Exemplo:
Kotlin
class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } }
Java
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } }
A vantagem dessa abordagem é que, ao contrário dos wake locks (discutidos em Manter a CPU ligada), ela não requer permissão especial, e a plataforma gerencia corretamente o usuário entre apps, sem que o app precise se preocupar em liberar recursos não utilizados.
Outra maneira de implementar isso é no arquivo XML de layout do app, usando o atributo android:keepScreenOn
:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:keepScreenOn="true"> ... </RelativeLayout>
O uso de android:keepScreenOn="true"
é equivalente a usar FLAG_KEEP_SCREEN_ON
.
Você pode usar a abordagem que for melhor para o app. A vantagem de definir a sinalização programaticamente na sua atividade é que ela oferece a opção de limpar a sinalização programaticamente mais tarde e, assim, permitir que a tela seja desativada.
Observação: você só precisa apagar a sinalização FLAG_KEEP_SCREEN_ON
se não quiser mais que a tela permaneça no app em execução, por exemplo, se você quiser que a tela atinja determinado período de inatividade. O gerenciador de janelas se encarrega de garantir que as coisas certas aconteçam quando o app entra em segundo plano ou retorna ao primeiro plano. Mas se você quiser limpar explicitamente a sinalização e permitir que a tela seja desativada novamente, use clearFlags()
: getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
.
Manter a CPU ligada
Se você precisa manter a CPU ligada para concluir algum trabalho antes que o dispositivo entre suspensão, pode usar um recurso de serviço do sistema PowerManager
chamado "wake locks". Os wake locks permitem que o app controle o estado de atividade do dispositivo host.
Criar e manter wake locks pode ter um impacto significativo na duração da bateria do dispositivo host. Portanto, use wake locks apenas quando estritamente necessário e os mantenha pelo menor tempo possível. Por exemplo, o ideal seria nunca precisar usar um wake lock em uma atividade. Conforme descrito acima, se você quiser manter a tela ativada na sua atividade, use FLAG_KEEP_SCREEN_ON
.
Um caso legítimo de uso de um wake lock pode ser um serviço em segundo plano que precisa pegar um wake lock para manter a CPU funcionando enquanto a tela está desligada. Novamente, essa prática precisa ser minimizada por causa do impacto sobre a duração da bateria.
Para usar um wake lock, a primeira etapa é adicionar a permissão WAKE_LOCK
ao arquivo de manifesto do seu app:
<uses-permission android:name="android.permission.WAKE_LOCK" />
Se o app inclui um broadcast receiver que usa um serviço para fazer algum trabalho, você pode gerenciar seu wake lock por meio de um WakefulBroadcastReceiver
, conforme descrito em Usar um broadcast receiver que mantenha o dispositivo ativado.
Essa é a abordagem preferida. Se o app não seguir esse padrão, veja como definir um wake lock diretamente:
Kotlin
val wakeLock: PowerManager.WakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run { newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag").apply { acquire() } }
Java
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag"); wakeLock.acquire();
Para liberar o wake lock, chame wakelock.release()
. Isso libera sua reivindicação para a CPU. É importante liberar um wake lock assim que o app terminar de usá-lo para evitar o esgotamento da bateria.
Usar um broadcast receiver que mantenha o dispositivo ativado
O uso de um broadcast receiver com um serviço permite que você gerencie o ciclo de vida de uma tarefa em segundo plano.
Um WakefulBroadcastReceiver
é um tipo especial de broadcast receiver que se encarrega de criar e gerenciar um PARTIAL_WAKE_LOCK
para o app. Um WakefulBroadcastReceiver
passa o trabalho para um Service
, normalmente um IntentService
, garantindo que o dispositivo não volte a entrar em suspensão na transição. Se você não mantiver um wake lock ao fazer a transição do trabalho para um serviço, estará efetivamente permitindo que o dispositivo entre novamente em suspensão antes que o trabalho seja concluído. O resultado é que o app pode não concluir o trabalho até algum momento arbitrário no futuro, que não é o que você quer.
A primeira etapa para usar um WakefulBroadcastReceiver
é adicioná-lo ao seu manifesto, como acontece com qualquer outro broadcast receiver:
<receiver android:name=".MyWakefulReceiver"></receiver>
O código a seguir inicia MyIntentService
com o método startWakefulService()
.
Esse método é comparável a startService()
, exceto pelo fato de que WakefulBroadcastReceiver
está mantendo um wake lock quando o serviço é iniciado. O intent que é transmitido com startWakefulService()
contém um extra que identifica o wake lock:
Kotlin
class MyWakefulReceiver : WakefulBroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // Start the service, keeping the device awake while the service is // launching. This is the Intent to deliver to the service. Intent(context, MyIntentService::class.java).also { service -> WakefulBroadcastReceiver.startWakefulService(context, service) } } }
Java
public class MyWakefulReceiver extends WakefulBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // Start the service, keeping the device awake while the service is // launching. This is the Intent to deliver to the service. Intent service = new Intent(context, MyIntentService.class); startWakefulService(context, service); } }
Quando o serviço é concluído, ele chama MyWakefulReceiver.completeWakefulIntent()
para liberar o wake lock. O método completeWakefulIntent()
tem como parâmetro o mesmo intent transmitido de WakefulBroadcastReceiver
:
Kotlin
const val NOTIFICATION_ID = 1 class MyIntentService : IntentService("MyIntentService") { private val notificationManager: NotificationManager? = null internal var builder: NotificationCompat.Builder? = null override fun onHandleIntent(intent: Intent) { val extras: Bundle = intent.extras // Do the work that requires your app to keep the CPU running. // ... // Release the wake lock provided by the WakefulBroadcastReceiver. MyWakefulReceiver.completeWakefulIntent(intent) } }
Java
public class MyIntentService extends IntentService { public static final int NOTIFICATION_ID = 1; private NotificationManager notificationManager; NotificationCompat.Builder builder; public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(Intent intent) { Bundle extras = intent.getExtras(); // Do the work that requires your app to keep the CPU running. // ... // Release the wake lock provided by the WakefulBroadcastReceiver. MyWakefulReceiver.completeWakefulIntent(intent); } }