Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

定期的に現在地の更新情報を受け取る

アプリが位置情報を継続的に追跡できると、より関連性の高い情報をユーザーに配信できます。たとえば、歩行中や運転中の道案内をするアプリや、貴重品の場所を追跡するアプリでは、定期的にデバイスから位置情報を取得する必要があります。地理的位置(緯度と経度)だけでなく、デバイスの向き(移動の水平方向)、高度、速度などの追加情報をユーザーに提供できます。この情報は、その他も含めて、アプリが融合された位置予測プロバイダから取得できる Location オブジェクトで利用できます。

直近の現在地情報の取得のレッスンで説明したように、getLastLocation() を使用してデバイスの位置情報を取得できますが、より直接的に、融合された位置予測プロバイダからの定期的な更新情報をリクエストする方法もあります。このリクエストに対して、API は、WiFi や GPS(グローバル ポジショニング システム)などのその時点で利用可能な位置情報プロバイダに基づいて、利用可能な最良の位置情報をアプリへ定期的に提供します。位置情報の精度は、プロバイダ、リクエストした位置情報の利用許可、位置情報リクエストに設定したオプションによって決まります。

このレッスンでは、融合された位置予測プロバイダの requestLocationUpdates() メソッドを使用してデバイスの位置情報の定期的な更新をリクエストする方法を説明します。

パーミッションを宣言する

位置情報サービスを使用するアプリは、位置情報の利用許可をリクエストする必要があります。ほとんどの場合、おおよその位置情報の利用許可をリクエストしても、利用可能な位置情報プロバイダから、かなり正確な位置情報を取得できます。

次のスニペットは、おおよその位置情報の利用許可をリクエストする方法を示しています。

    <manifest ... >
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    </manifest>
    
ユーザー向けダイアログのスクリーンショット
図 1. アプリが、アプリの使用中にのみ位置情報にアクセスするという選択肢を示して、位置情報をリクエストするときに表示するダイアログ

Android 10(API レベル 29)以降を搭載したデバイスでは、図 1 のダイアログを表示して、アプリが位置情報の利用許可をリクエストしていることを知らせます。ユーザーがこのダイアログから、アプリがデバイスの位置情報にアクセスするのを許可した場合、アプリはユーザーがアプリを操作している間のみ位置情報にアクセスでき、バックグラウンドで実行しているときはアクセスできません。ユーザーがアプリをバックグラウンドに送った後に、ユーザー開始型アクションの継続を行って、アプリが位置情報の詳細を取得するのを許可し、フォアグラウンド サービスであると宣言できます。

注: Android 10 以降で動作しているアプリでは、バックグラウンド位置情報の利用許可をリクエストできますが、使用しないことを強くおすすめします。

直近の現在地情報を取得する

デバイスの直近の現在地情報は、アプリが位置情報の定期的な更新を開始する前に必ず既知の位置情報を持つことになるため、出発点として便利です。直近の現在地情報の取得のレッスンでは、getLastLocation() を呼び出して直近の現在地情報を取得する方法を説明します。次のセクションのスニペットでは、アプリは直近の現在地情報を取得済みで、それをグローバル変数 mCurrentLocationLocation オブジェクトとして保存しているものとします。

現在地の更新情報をリクエストする

アプリは、位置情報の更新をリクエストする前に、位置情報サービスに接続して位置情報のリクエストを行う必要があります。その方法は、位置情報の設定の変更のレッスンで説明します。位置情報のリクエストが完了したら、requestLocationUpdates() を呼び出して定期的な更新を開始できます。

リクエストの形式によって、融合された位置予測プロバイダが LocationCallback.onLocationResult() コールバック メソッドを呼び出して Location オブジェクトのリストを渡す場合と、拡張データに位置情報を含んだ PendingIntent を発行する場合とがあります。更新の精度と頻度は、リクエストした位置情報の利用許可と、位置情報リクエスト オブジェクトに設定したオプションによって変わります。

このレッスンでは、LocationCallback コールバックを使用して更新情報を受け取る方法を説明します。requestLocationUpdates() を呼び出し、その際に LocationRequest オブジェクトのインスタンスと LocationCallback を渡します。次のコードサンプルのように、startLocationUpdates() メソッドを定義します。

Kotlin

    override fun onResume() {
        super.onResume()
        if (requestingLocationUpdates) startLocationUpdates()
    }

    private fun startLocationUpdates() {
        fusedLocationClient.requestLocationUpdates(locationRequest,
                locationCallback,
                null /* Looper */)
    }
    

Java

    @Override
    protected void onResume() {
        super.onResume();
        if (requestingLocationUpdates) {
            startLocationUpdates();
        }
    }

    private void startLocationUpdates() {
        fusedLocationClient.requestLocationUpdates(locationRequest,
                locationCallback,
                null /* Looper */);
    }
    

上のコード スニペットでは requestingLocationUpdates というブール値のフラグを参照していますが、これはユーザーが位置情報の更新をオンまたはオフに切り替えたかどうかを追跡するために使用されます。ユーザーが位置情報の更新をオフにした場合、アプリの位置情報に関する要件をユーザーに通知できます。このブール値のフラグをアクティビティのインスタンス間で保持する方法については、アクティビティの状態の保存をご覧ください。

位置情報更新のコールバックを定義する

融合された位置予測プロバイダは LocationCallback.onLocationResult() コールバック メソッドを呼び出します。その引数には、位置情報の緯度と経度が入った Location オブジェクトのリストが含まれます。以下のスニペットで、LocationCallback インターフェースの実装とそのメソッドの定義の方法を示します。次に、位置情報更新のタイムスタンプを取得し、アプリのユーザー インターフェースに緯度、経度、タイムスタンプを表示します。

Kotlin

    private lateinit var locationCallback: LocationCallback

    // ...

    override fun onCreate(savedInstanceState: Bundle?) {
        // ...

        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                locationResult ?: return
                for (location in locationResult.locations){
                    // Update UI with location data
                    // ...
                }
            }
        }
    }
    

Java

    private LocationCallback locationCallback;

    // ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...

        locationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                if (locationResult == null) {
                    return;
                }
                for (Location location : locationResult.getLocations()) {
                    // Update UI with location data
                    // ...
                }
            };
        };
    }
    

バックグラウンド位置情報へのアクセス権をリクエストする

Android 10 以降をターゲットにしているアプリの場合、アプリのマニフェストで ACCESS_BACKGROUND_LOCATION パーミッションを宣言して、バックグラウンドでの実行中に位置情報の更新を定期的に受信するためにユーザーの許可を得る必要があります。

以下のコードスニペットで、アプリでバックグラウンド位置情報へのアクセス権をリクエストする方法を示します。

    <manifest ... >
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    </manifest>
    
ユーザー向けダイアログのスクリーンショット
図 2. アプリが、バックグラウンドでの実行中も含めて常に位置情報にアクセスするという選択肢を示して、位置情報をリクエストするときに表示するダイアログ

Android 10(API レベル 29)以降を搭載したデバイスでは、図 2 を表示して、アプリが位置情報の利用許可をリクエストし、さらにバックグラウンドも含めて常に位置情報にアクセスする許可をリクエストしていることを示します。このダイアログには、アプリの使用中にのみ位置情報へのアクセスを許可するという選択肢もあります。これが選択された場合、バックグラウンドにあるアプリは位置情報にアクセスすることができません。アプリのワークフローとして位置情報へのアクセスが常に必要な場合、ユーザーにバックグラウンド位置情報のリクエストを通知する必要があります。

注意: ユーザーが、最初にバックグラウンドのアプリによる位置情報の利用を許可しても、後ほど、システム設定からこの許可を取り消すことができます。アプリに対して、アプリの実行中にだけ位置情報へのアクセスを許可することも、まったく許可しないこともできます。

そのため、アプリはサービスを起動するたびに、バックグラウンドで位置情報にアクセスすることが引き続き許可されているかを確認してください。

バックグラウンド位置情報へのアクセスを知らせるユーザー向けリマインダー

システム通知のスクリーンショット
図 3. デバイスの位置情報への「常時」アクセス権限がアプリに付与されていることを知らせる通知

ユーザーは、デバイスの位置情報への常時アクセス権限をアプリに付与できます。ユーザーがそのような選択をした後、最初にアプリがバックグラウンドでデバイスの位置情報にアクセスすると、システムによりユーザーへの通知の送信がスケジューリングされます。この通知は、デバイスの位置情報への常時アクセスがアプリに許可されていることをユーザーに知らせるものです。図 3 に示す通知の例をご覧ください。

バックグラウンド位置情報が必要であることをユーザーに通知する

ユーザーが、アプリの使用中にのみ位置情報にアクセスすることをリクエストした場合、カスタム ダイアログを表示して、位置情報への常時アクセスがなければアプリのワークフローが正しく機能しないことを通知してください。

注意: ユーザーは、デバイスの位置情報に対するアクセスを拒否すると同時に、今後アプリがデバイスの位置情報に対するアクセス権限をリクエストしないように指定できます。アプリは、この「許可しない(次回から表示しない)」という決定を尊重して処理する必要があります。

ユーザーがこのダイアログを承認すると、図 4 に示すシステム ダイアログが表示されて、バックグラウンド位置情報をリクエストできるようになります。

ユーザー向けダイアログのスクリーンショット
図 4. 位置情報への常時アクセス権限に関してユーザーの同意をリクエストするダイアログ

このアクセス権限確認ロジックの例を、次のコード スニペットに示します。

Kotlin

    val permissionAccessCoarseLocationApproved = ActivityCompat
        .checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED

    if (permissionAccessCoarseLocationApproved) {
       val backgroundLocationPermissionApproved = ActivityCompat
           .checkSelfPermission(this, permission.ACCESS_BACKGROUND_LOCATION) ==
           PackageManager.PERMISSION_GRANTED

       if (backgroundLocationPermissionApproved) {
           // App can access location both in the foreground and in the background.
           // Start your service that doesn't have a foreground service type
           // defined.
       } else {
           // App can only access location in the foreground. Display a dialog
           // warning the user that your app must have all-the-time access to
           // location in order to function properly. Then, request background
           // location.
           ActivityCompat.requestPermissions(this,
               arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
               your-permission-request-code
           )
       }
    } else {
       // App doesn't have access to the device's location at all. Make full request
       // for permission.
       ActivityCompat.requestPermissions(this,
           arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION,
                   Manifest.permission.ACCESS_BACKGROUND_LOCATION),
           your-permission-request-code
       )
    }
    

Java

    boolean permissionAccessCoarseLocationApproved =
        ActivityCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION)
            == PackageManager.PERMISSION_GRANTED;

    if (permissionAccessCoarseLocationApproved) {
       boolean backgroundLocationPermissionApproved =
               ActivityCompat.checkSelfPermission(this,
                   permission.ACCESS_BACKGROUND_LOCATION)
                   == PackageManager.PERMISSION_GRANTED;

       if (backgroundLocationPermissionApproved) {
           // App can access location both in the foreground and in the background.
           // Start your service that doesn't have a foreground service type
           // defined.
       } else {
           // App can only access location in the foreground. Display a dialog
           // warning the user that your app must have all-the-time access to
           // location in order to function properly. Then, request background
           // location.
           ActivityCompat.requestPermissions(this, new String[] {
               Manifest.permission.ACCESS_BACKGROUND_LOCATION},
               your-permission-request-code);
       }
    } else {
       // App doesn't have access to the device's location at all. Make full request
       // for permission.
       ActivityCompat.requestPermissions(this, new String[] {
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.ACCESS_BACKGROUND_LOCATION
            },
            your-permission-request-code);
    }
    

ユーザー開始型アクションを継続する

アプリから、運転中のターンバイターン ナビゲーションや、経路に沿ってランニングするといった位置情報に依存するワークフローを提供する場合があります。ユーザーがこういった種類のタスクを実行する場合、ホームボタンが押される、あるいはディスプレイをオフにされるなどの結果、アプリがバックグラウンドへ送られ、バックグラウンドでの動作になってからデバイスの位置情報にアクセスする必要の生じることがあります。

このような特殊なユースケースでデバイス位置情報へのアクセス権限を維持するには、アプリのマニフェスト内でフォアグラウンド サービスのタイプ"location" であると宣言して、フォアグラウンド サービスを起動します。

    <service
        android:name="MyNavigationService"
        android:foregroundServiceType="location" ... >
        ...
    </service>
    

フォアグラウンド サービスを起動する前に、アプリがまだデバイス位置情報にアクセスできることを確認する必要があります。

Kotlin

    val permissionAccessCoarseLocationApproved = ActivityCompat
        .checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) ==
        PackageManager.PERMISSION_GRANTED

    if (permissionAccessCoarseLocationApproved) {
       // App has permission to access location in the foreground. Start your
       // foreground service that has a foreground service type of "location".
    } else {
       // Make a request for foreground-only location access.
       ActivityCompat.requestPermissions(this,
           arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
           your-permission-request-code
       )
    }
    

Java

    boolean permissionAccessCoarseLocationApproved =
        ActivityCompat.checkSelfPermission(this,
            permission.ACCESS_COARSE_LOCATION) ==
            PackageManager.PERMISSION_GRANTED;

    if (permissionAccessCoarseLocationApproved) {
        // App has permission to access location in the foreground. Start your
        // foreground service that has a foreground service type of "location".
    } else {
       // Make a request for foreground-only location access.
       ActivityCompat.requestPermissions(this, new String[] {
            Manifest.permission.ACCESS_COARSE_LOCATION},
           your-permission-request-code);
    }
    

位置情報の更新を停止する

ユーザーが別のアプリや同じアプリの別のアクティビティに切り替えた場合など、アクティビティが直接操作されなくなったときに、位置情報の更新を停止するかどうかを検討してください。アプリがバックグラウンドで実行されている場合でも情報を収集する必要がなければ、停止することで簡単に消費電力を削減できます。このセクションでは、アクティビティの onPause() メソッドで更新を停止する方法を示します。

位置情報の更新を停止するには、次のコードサンプルのように、removeLocationUpdates() を呼び出して LocationCallback を渡します。

Kotlin

    override fun onPause() {
        super.onPause()
        stopLocationUpdates()
    }

    private fun stopLocationUpdates() {
        fusedLocationClient.removeLocationUpdates(locationCallback)
    }
    

Java

    @Override
    protected void onPause() {
        super.onPause();
        stopLocationUpdates();
    }

    private void stopLocationUpdates() {
        fusedLocationClient.removeLocationUpdates(locationCallback);
    }
    

ブール値 mRequestingLocationUpdates を使用して、位置情報の更新がオンになっているかどうかを追跡します。アクティビティの onResume() メソッドで、位置情報の更新が有効になっているか確認し、無効の場合は有効にします。

Kotlin

    override fun onResume() {
        super.onResume()
        if (requestingLocationUpdates) startLocationUpdates()
    }
    

Java

    @Override
    protected void onResume() {
        super.onResume();
        if (requestingLocationUpdates) {
            startLocationUpdates();
        }
    }
    

アクティビティの状態を保存する

画面の向きや言語などのデバイスの構成を変更すると、現在のアクティビティに不具合が発生する可能性があります。したがって、アプリはアクティビティの再作成に必要な情報を保存しておく必要があります。これを行う方法の 1 つは、Bundle オブジェクトに保存されているインスタンス状態を使用する方法です。

次のコードサンプルで、アクティビティの onSaveInstanceState() コールバックを使用してインスタンス状態を保存する方法を示します。

Kotlin

    override fun onSaveInstanceState(outState: Bundle?) {
        outState?.putBoolean(REQUESTING_LOCATION_UPDATES_KEY, requestingLocationUpdates)
        super.onSaveInstanceState(outState)
    }
    

Java

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putBoolean(REQUESTING_LOCATION_UPDATES_KEY,
                requestingLocationUpdates);
        // ...
        super.onSaveInstanceState(outState);
    }
    

updateValuesFromBundle() メソッドを定義して、アクティビティの以前のインスタンスから保存された値(利用可能な場合)を復元します。次のコードサンプルのように、このメソッドをアクティビティの onCreate() メソッドから呼び出します。

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        // ...
        updateValuesFromBundle(savedInstanceState)
    }

    private fun updateValuesFromBundle(savedInstanceState: Bundle?) {
        savedInstanceState ?: return

        // Update the value of requestingLocationUpdates from the Bundle.
        if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) {
            requestingLocationUpdates = savedInstanceState.getBoolean(
                    REQUESTING_LOCATION_UPDATES_KEY)
        }

        // ...

        // Update UI to match restored state
        updateUI()
    }
    

Java

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // ...
        updateValuesFromBundle(savedInstanceState);
    }

    private void updateValuesFromBundle(Bundle savedInstanceState) {
        if (savedInstanceState == null) {
            return;
        }

        // Update the value of requestingLocationUpdates from the Bundle.
        if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) {
            requestingLocationUpdates = savedInstanceState.getBoolean(
                    REQUESTING_LOCATION_UPDATES_KEY);
        }

        // ...

        // Update UI to match restored state
        updateUI();
    }
    

インスタンス状態の保存について詳しくは、Android の Activity クラスのリファレンスをご覧ください。

注: より永続的なストレージが必要な場合、アプリの SharedPreferences にユーザーの設定を保存できます。アクティビティの onPause() メソッドで共有設定を設定し、onResume() で設定を取得します。設定の保存に関して詳しくは、Key-Value セットの保存をご覧ください。

次のレッスンの現在地の住所の表示では、特定の場所の番地を表示する方法を示します。

参考情報

詳細については、以下のリソースをご覧ください。

サンプル