The Android Developer Challenge is back! Submit your idea before December 2.

位置情報を最適化して電池の消耗を抑える

Android 8.0(API レベル 26)で導入されたバックグラウンド位置情報の制限により、位置情報サービスの電池消費量への影響が改めて注目されるようになりました。このページでは、位置情報サービスのベスト プラクティスと、アプリの電池の消費量を抑えるために今すぐできることについて説明します。ここで説明するベスト プラクティスを適用すると、アプリが稼働しているプラットフォームのバージョンに関係なくアプリの向上につながります。

Android 8.0 のバックグラウンド位置情報の制限に伴い、以下の変更が導入されました。

  • バックグラウンドでの位置情報の計算と配信が 1 時間あたり数回のみに調整されました。
  • Wi-Fi スキャンの頻度が抑えられ、端末が同一の静的アクセス ポイントに接続している場合は位置情報のアップデートが計算されなくなりました。
  • ジオフェンスの応答性が数十秒から約 2 分に変更されました。この変更により、電池のパフォーマンスが著しく向上しました。一部の端末ではパフォーマンスが最大で 10 倍向上しました。

このページでは、Google 位置情報サービス API を使用していることを前提としています。これらの API は、フレームワーク ロケーション API よりも精度が高く、電池の消費量が軽減されます。このページでは特に、融合された予測プロバイダ API に関する知識を前提としています。これは、GPS、Wi-Fi、セルラー ネットワークからのシグナルや、加速度計、ジャイロスコープ、磁力計などのセンサーからのシグナルを組み合わせる API です。融合された予測プロバイダ API を基盤に構築されており、電池のパフォーマンスのために最適化されているジオフェンス API に関する知識も必要です。

電池の消費について

位置情報の収集と電池の消費は、以下の点と直接関係しています。

  • 精度:位置情報データの精度。一般には、精度が高いほど電池の消費量が増加します。
  • 頻度:位置情報の計算頻度。位置情報の計算頻度が高いほど、電池の消費量が増加します。
  • 遅延:位置情報の配信速度。一般的には、遅延が少ないほど、必要とする電池の消費量が増加します。

精度

位置情報の精度を指定するには、setPriority() メソッドを使用し、次の値のうちの 1 つを引数として渡します。

  • PRIORITY_HIGH_ACCURACY では、位置情報の精度が最も高くなります。この場合、位置情報は必要な数の入力を使用して計算され(GPS、Wi-Fi、セルラーが有効になり、さまざまなセンサーが使用されます)、電池の消費量が大幅に増加する可能性があります。
  • PRIORITY_BALANCED_POWER_ACCURACY では、正確な位置情報が提供され、かつ電力消費が最適化されます。GPS が使用されることはほとんどありません。端末の位置情報の計算には、通常 Wi-Fi とセルの情報の組み合わせが使用されます。
  • PRIORITY_LOW_POWER では携帯電話の基地局に大きく依存し、GPS と Wi-Fi 入力を使用せずに、最小の電池消費量で低精度(市町村レベル)の情報を提供します。
  • PRIORITY_NO_POWER では、位置情報がすでに計算されている他のアプリから位置情報をパッシブに受け取ります。

ほとんどのアプリでは、バランスのとれた電力オプションまたは低電力オプションを使用して位置情報のニーズに対応できます。高精度オプションはフォアグラウンドで実行中のアプリで使用するようにしてください。このオプションを使用する場合は、リアルタイムの位置情報アップデートが必要となります(マッピング アプリなど)。

頻度

位置情報の収集頻度は、2 つの方法で指定できます。

  • 各自のアプリ用に位置情報を計算する頻度を指定するには、setinterval() メソッドを使用します。
  • 他のアプリで計算された位置情報を各自のアプリに配信する頻度を指定するには、setFastestInterval() を使用します。

setInterval() を使用するときには、可能な限り大きな値を渡してください。これは特に、不必要な電池消費の原因となることが多いバックグラウンドでの位置情報収集に該当します。間隔を数秒間に設定するのは、フォアグラウンドでの使用の場合のみにしてください。Android 8.0 で導入されたバックグラウンド位置情報の制限に伴いこれらの方針が適用されますが、Android 7.0 以前の端末にも適用されるようにアプリで取り組む必要があります。

遅延

遅延を指定するには、setMaxWaitTime() メソッドを使用し、setInterval() メソッドで指定した間隔よりも数倍大きな値を渡します。この設定により、位置情報の配信が遅延し、複数の位置情報アップデートがバッチで配信されます。この 2 つの変更により、電池の消費量を最小限に抑えることができます。

アプリで位置情報をすぐに更新する必要がない場合は、できる限り大きな値を setMaxWaitTime() メソッドに渡す必要があります。これにより遅延が発生しますが、より多くのデータを取得し、電池をより効率的に消費できます。

ジオフェンスを使用する場合、アプリは電力を節約するため大きな値を setNotificationResponsiveness() メソッドに渡すべきです。値として 5 分以上を指定することが推奨されます。

位置情報のユースケース

このセクションでは、一般的な位置情報収集のシナリオと、ジオフェンス API および融合された位置予測プロバイダ API の最適な使用に関する推奨事項について説明します。

ユーザーから見えるアップデートまたはフォアグラウンドでのアップデート

例:非常に短い遅延で頻繁かつ正確なアップデートを必要とするマッピング アプリ。すべてのアップデートはフォアグラウンドで行われます。ユーザーがアクティビティを開始し、位置情報データを使用し、しばらくしてからアクティビティを停止します。

PRIORITY_HIGH_ACCURACY または PRIORITY_BALANCED_POWER_ACCURACY を指定した setPriority() メソッドを使用します。

setInterval() メソッドに指定する間隔は、ユースケースに応じて異なります。リアルタイム シナリオの場合は値を数秒に設定します。それ以外の場合は、数分間に制限します(電池の使用量を最小限に抑えるため、約 2 分以上が推奨されます)。

端末の位置を確認する

例:端末の位置情報の確認を求める天気予報アプリ。

getLastLocation() メソッドを使用します。このメソッドは、最新の利用可能な位置情報を返します(稀に位置情報が Null の場合があります)。このメソッドでは位置情報を最も簡単に取得でき、位置情報アップデートのアクティブなリクエストに伴うコストが発生しません。isLocationAvailable() メソッドと組み合わせて使用します。このメソッドは、getLastLocation() から返される位置情報が十分に新しい場合に true を返します。

ユーザーが特定の位置にいる場合にアップデートを開始する

例:ユーザーが職場、自宅、あるいはその他の場所から特定の距離内に位置している場合にアップデートをリクエストする。

ジオフェンスを、融合された位置予測プロバイダのアップデートと組み合わせて使用します。アプリがジオフェンス開始トリガーを受信すると更新をリクエストし、アプリがジオフェンス終了トリガーを受信すると更新を削除します。これにより、定義されている領域にユーザーが入った場合にのみ、アプリがより詳細な位置情報アップデートを取得できるようになります。

このシナリオの一般的なワークフローでは、ジオフェンスが遷移を開始した時点での通知の表示、ユーザーが通知をタップしたときにアップデートをリクエストするコードを含むアクティビティの実行などが行われることがあります。

ユーザーのアクティビティ状態に基づいてアップデートを開始する

例:ユーザーがバイクを運転またはバイクに乗っている場合にのみアップデートをリクエストする。

Activity Recognition API を、融合された位置予測プロバイダのアップデートと組み合わせて使用します。対象となるアクティビティが検出された時点でアップデートをリクエストし、ユーザーがそのアクティビティの実行を停止した時点でアップデートを削除します。

このユースケースの一般的なワークフローでは、検出されたアクティビティの通知の表示と、ユーザーが通知をタップしたときにアップデートをリクエストするコードを含むアクティビティの実行などが行わることがあります。

地理的領域に関連付けられたバックグラウンド位置情報アップデートを長期実行する

例:ユーザーが、特定の小売店舗付近に端末が位置している場合に通知を受信することを求めている。

これは、ジオフェンスの非常に優れたユースケースです。このユースケースではバックグラウンド位置情報が確実に関連しているため、addGeofences(GeofencingRequest, PendingIntent) メソッドを使用します。

以下の構成オプションを設定する必要があります。

  • 滞在の遷移を追跡する場合は、setLoiteringDelay() メソッドを使用し、値として約 5 分以内の値を渡します。

  • setNotificationResponsiveness() を使用し、値として約 5 分以内の値を渡します。ただし、アプリが応答での追加の遅延を管理できる場合は、値として約 10 分を使用することを検討してください。

アプリが一度に登録できるジオフェンスの数は最大 100 個までです。アプリが多数の小売店オプションを追跡するユースケースでは、アプリが大きなジオフェンス(市町村レベル)を登録し、大きなジオフェンス内の小売店舗に対して小さなジオフェンス(市町村内の位置)を動的に登録することができます。ユーザーが大きなジオフェンスに入ったら、小さなジオフェンスを追加できます。ユーザーが大きなジオフェンスから出たら、小さなジオフェンスを削除して、新しい領域のジオフェンスを再登録できます。

アプリ コンポーネントが見えない状態でバックグラウンド位置情報アップデートを長期実行する

例:位置情報をパッシブに追跡するアプリ

可能であれば、PRIORITY_NO_POWER オプションを指定した setPriority() メソッドを使用してください。この場合、ほとんど電池を消費しません。PRIORITY_NO_POWER を使用できない場合は、PRIORITY_BALANCED_POWER_ACCURACY または PRIORITY_LOW_POWER を使用してください。ただし、バックグラウンド処理を維持するため PRIORITY_HIGH_ACCURACY は使用しないでください。このオプションは電池を大量に消費します。

より多くの位置情報データが必要な場合は、パッシブな位置情報を使用するため、setFastestInterval() メソッドを呼び出し、setInterval() に渡す値よりも小さい値を渡してください。PRIORITY_NO_POWER オプションと組み合わせることで、パッシブな位置情報が、他のアプリにより計算された位置情報を追加のコストなしに利用して配信できます。

setMaxWaitTime() メソッドを使用していくらかの遅延を追加し、適度な頻度にします。たとえば、値として約 10 分を指定した setinterval() メソッドを使用する場合は、値として 30 ~ 60 分を指定した setMaxWaitTime() を呼び出すことを検討します。これらのオプションを使用すると、アプリに対し位置情報が約 10 分間隔で計算されますが、アプリは 30 ~ 60 分間隔で起動し、位置情報データはバッチ アップデートとして使用できます。この方法では、遅延と引き換えに、使用可能なデータが増え、電池のパフォーマンスが向上します。

ユーザーが他のアプリを操作している間の頻繁な高精度のアップデート

例:ユーザーが画面をオフにするかまたは別のアプリを開いても引き続き動作するナビゲーション アプリまたはフィットネス アプリ。

フォアグラウンド サービスを使用します。アプリがユーザーに代わって負荷の高い処理を行う可能性がある場合の推奨されるベスト プラクティスは、ユーザーがその処理を認識できるようにすることです。フォアグラウンド サービスでは継続的な通知が必要です。詳細については、通知の概要 をご覧ください。

位置情報のベスト プラクティス

このセクションで紹介するベスト プラクティスを導入すると、アプリによる電池の使用量を低減できます。

位置情報のアップデートを削除する

不必要な電池の消費の一般的な原因として、不要になった位置情報アップデートを削除できなかったことがあります。この状況が発生するのは、たとえばアクティビティの onStart() または onResume() ライフサイクル メソッドに、requestlocationUpdates() の呼び出しが含まれているが、対応する removeLocationUpdates() の呼び出しが onPause() または onStop() ライフサイクル メソッドに含まれていない場合です。

ライフサイクル対応コンポーネントを使用して、アプリでのアクティビティのライフサイクルを適切に管理できます。詳細については、ライフサイクル対応コンポーネントによるライフサイクルの処理をご覧ください。

タイムアウトを設定する

電池の消費を防ぐには、位置情報アップデートを停止する必要がある時点で適切なタイムアウトを設定します。タイムアウトにより、アップデートが無限に継続されることがなくなり、(コードのバグなどが原因で)アップデートがリクエストされたが削除されていない場合にアプリが保護されます。

融合された位置予測プロバイダのリクエストの場合は、setExpirationDuration() を呼び出してタイムアウトを追加します。このメソッドは、メソッドが最後に呼び出された時点からの経過時間をミリ秒単位で表すパラメータを受け取ります。タイムアウトを追加するもう 1 つの方法として、setExpirationTime() を呼び出す方法があります。このメソッドは、システムの最終起動以降の有効期間をミリ秒単位で表すパラメータを受け取ります。

ジオフェンス位置情報リクエストにタイムアウトを追加するには、setExpirationDuration() メソッドを呼び出します。

バッチ リクエスト

フォアグラウンド以外のすべてのユースケースでは、複数のリクエストをまとめてバッチとして処理します。setInterval() メソッドを使用して、位置情報の計算間隔を指定できます。次に setMaxWaitTime() メソッドを使用して、位置情報をアプリに配信する間隔を設定します。setMaxWaitTime() メソッドに渡す値は、setInterval() メソッドに渡される値の倍数にしてください。たとえば、次の位置情報リクエストを考えてください。

Kotlin

val request = LocationRequest()
request.setInterval(10 * 60 * 1000)
request.setMaxWaitTime(60 * 60 * 1000)

Java

LocationRequest request = new LocationRequest();
request.setInterval(10 * 60 * 1000);
request.setMaxWaitTime(60 * 60 * 1000);

この場合、位置情報は約 10 分ごとに計算され、約 6 つの位置情報データ ポイントがバッチで約 1 時間ごとに配信されます。位置情報アップデートを 10 分ごとに取得しますが、端末は 1 時間ごとにのみ起動するので電池を節約できます。

パッシブな位置情報アップデートを使用する

バックグラウンドのユースケースでは、位置情報のアップデートを制限することをお勧めします。これは Android 8.0 の制限により適用されますが、8.0 よりも古い端末で実行されるアプリでは、可能な限りバックグラウンド位置情報を制限するように取り組む必要があります。

各自のアプリがバックグラウンドで実行され、別のアプリがフォアグラウンドで頻繁に位置情報アップデートをリクエストすることがあります。位置情報サービスにより、各自のアプリに対しこのアップデートが使用可能になります。位置情報データを適宜消費する次の位置情報リクエストを検討してください。

Kotlin

val request = LocationRequest()
request.setInterval(15 * 60 * 1000)
request.setFastestInterval(2 * 60 * 1000)

Java

LocationRequest request = new LocationRequest();
request.setInterval(15 * 60 * 1000);
request.setFastestInterval(2 * 60 * 1000);

前の例では、アプリに対し位置情報が約 15 分ごとに計算されました。他のアプリが位置情報をリクエストする場合、データは最大 2 分間隔で各自のアプリに対し使用可能になります。

位置情報をパッシブに消費する場合は電池の消費は発生しませんが、位置情報データを受信すると負荷の高い CPU または I/O 操作が発生するケースに十分に注意してください。電池のコストを最小限に抑えるために、setFastestInterval() に指定する間隔が短すぎることがないようにしてください。

このページに記載されている推奨事項に従うことで、ユーザーの端末の電池パフォーマンスを大幅に向上できます。ユーザーが、電池を消費しないアプリを削除する可能性はそれほどありません。