デバイスの起動状態を維持する

電池の消耗を防ぐため、アイドル状態のままになっている Android デバイスはすぐにスリープ状態になります。しかし、アプリで画面や CPU のスリープを解除したり、処理が完了するまでスリープ状態に移行しないようにしたりしなければならない場合もあります。

アプリのニーズは、デベロッパーが採用するアプローチによって決まります。ただし、一般的な経験則として、アプリにとってできる限り軽量なアプローチを採用し、アプリがシステム リソースに及ぼす影響を最小限に抑える必要があります。以下のセクションでは、デバイスのデフォルトのスリープ動作がアプリの要件と適合しない場合の対処方法について説明します。

wake lock の使用に代わる方法

wake lock のサポートをアプリに追加する前に、アプリのユースケースが以下の代替ソリューションのいずれかをサポートしているかどうかを確認します。

  • 実行時間が長い HTTP ダウンロードをアプリが実行している場合は、DownloadManager を使用することを検討してください。
  • アプリが外部サーバーのデータを同期している場合は、同期アダプターを作成することを検討してください。
  • アプリがバックグラウンド サービスを利用している場合は、JobScheduler または Firebase Cloud Messaging を使用して、それらのサービスを特定の間隔でトリガーすることを検討してください。

画面をオンのままにする

特定のアプリ(ゲームや映画アプリなど)では画面をオンのままにする必要があります。その場合、アクティビティで FLAG_KEEP_SCREEN_ON を使用することをおすすめします(ただし、アクティビティでのみ使用し、サービスや他のアプリ コンポーネントでは使用しないでください)。次に例を示します。

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);
      }
    }
    

このアプローチのメリットは、wake lock とは異なり(CPU をオンのままにするを参照)、特別な権限が不要で、アプリ間を移動するユーザーをプラットフォームが適切に管理することです。使用されていないリリースの解放をアプリで行う必要はありません。

もう 1 つの方法は、アプリのレイアウト XML ファイルで 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>

android:keepScreenOn="true" の使用方法は FLAG_KEEP_SCREEN_ON と同じです。アプリに適している方のアプローチをどちらでも使用できます。アクティビティ内でプログラムによってフラグを設定するメリットは、フラグを後でプログラムによってクリアし、それによって画面をオフにできる点です。

注: 実行中のアプリで画面をオンのままにする必要がなくなった場合(非アクティブの状態が一定期間経過した後に画面をタイムアウトする場合など)を除き、FLAG_KEEP_SCREEN_ON フラグをクリアする必要はありません。アプリがバックグラウンドに移動するか、またはフォアグラウンドに戻ってくると、ウィンドウ マネージャーが適切な処理が行われるよう対処します。ただし、フラグを明示的にクリアし、それによって画面を再度オフにできるようにしたい場合は、次のように clearFlags() を使用します。 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

CPU をオンのままにする

デバイスがスリープ状態になる前に処理を完了させるために CPU を動作させ続ける必要がある場合は、wake lock と呼ばれる PowerManager のシステム サービス機能を使用できます。wake lock を使用すると、ホストデバイスの電力状態をアプリで管理できます。

wake lock を作成して保持すると、ホストデバイスの電池寿命に大きな影響を及ぼすことがあります。そのため、wake lock は厳密に必要な場合にのみ使用し、保持期間をできるだけ短くする必要があります。たとえば、アクティビティで wake lock を使用する必要はありません。前述のとおり、アクティビティ内で画面をオンにしたままにする場合は、FLAG_KEEP_SCREEN_ON を使用します。

wake lock を使用するのに適したケースの 1 つとしてバックグラウンド サービスがあります。バックグラウンド サービスでは、wake lock を取得して CPU を動作させ続け、画面がオフになっている間に処理を行う必要があります。ただし、バックグラウンド サービスも電池寿命に影響を及ぼすため、できるだけ利用しないようにする必要があります。

wake lock を使用するには、まず、WAKE_LOCK 権限をアプリのマニフェスト ファイルに追加します。

<uses-permission android:name="android.permission.WAKE_LOCK" />

サービスを利用してなんらかの処理を行うブロードキャスト レシーバがアプリに含まれている場合は、ブロードキャスト レシーバを使用してデバイスがスリープ状態に移行しないようにするで説明するように、WakefulBroadcastReceiver を介して wake lock を管理できます。このアプローチを採用することをおすすめします。アプリがこのパターン以外の場合は、wake lock を直接設定します。以下にその方法を示します。

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();
    

wake lock を解放するには、wakelock.release() を呼び出します。これにより、CPU に対する要求が取り下げられます。電池の消耗を防ぐには、アプリが wake lock を使い終わったらすぐに解放することが重要です。

ブロードキャスト レシーバを使用してデバイスがスリープ状態に移行しないようにする

ブロードキャスト レシーバをサービスと組み合わせて使用すると、バックグラウンド タスクのライフサイクルを管理できます。

WakefulBroadcastReceiver は特殊なタイプのブロードキャスト レシーバで、アプリ用の PARTIAL_WAKE_LOCK の作成と管理を行います。WakefulBroadcastReceiverService(通常は IntentService)に処理を渡し、その間にデバイスがスリープ状態に戻らないようにします。処理をサービスに移行している間に wake lock を保持しなくても、処理が完了する前にデバイスがスリープ状態に効果的に戻れるようにすることができます。最終的に、将来の任意の時点までアプリで処理を完了できない場合もあります(これは望ましいことではありません)。

WakefulBroadcastReceiver を使用する場合は、他のブロードキャスト レシーバと同様に、まずマニフェストに追加します。

<receiver android:name=".MyWakefulReceiver"></receiver>

次のコードでは、startWakefulService() メソッドを使用して MyIntentService を開始しています。このメソッドは、サービスの開始時に WakefulBroadcastReceiver が wake lock を保持していることを除き、startService() と同じです。startWakefulService() で渡されるインテントに 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);
        }
    }
    

サービスの終了時に MyWakefulReceiver.completeWakefulIntent() が呼び出され、wake lock が解放されます。completeWakefulIntent() メソッドは、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);
        }
    }