Kotlinを使用してAndroidで位置情報の更新を受信する

Android 10および11を使用すると、ユーザーはアプリのデバイスの場所へのアクセスをより細かく制御できます。

Android 11で実行されているアプリが位置情報へのアクセスを要求する場合、ユーザーには次の4つのオプションがあります。

  • 常に許可する
  • アプリの使用中にのみ許可する(Android 10の場合)
  • 1回のみ(Android 11の場合)
  • 拒否

Android 10

75c80ddc7fa6bedc.png

Android 11

bcfb2062f7e9048d.png

このコードラボでは、位置情報の更新を受信する方法と、Androidの任意のバージョン(特にAndroid 10および11)で位置情報をサポートする方法を学習します。コードラボの最後に、取得するための現在のベストプラクティスに従ったアプリがあると期待できます。場所の更新。

前提条件

あなたがすること

  • Androidでの位置情報のベストプラクティスに従ってください。
  • フォアグラウンドロケーション権限を処理します(アプリの使用中にユーザーがアプリにデバイスロケーションへのアクセスを要求した場合)。
  • 既存のアプリを変更して、場所の登録と登録解除のコードを追加することで、場所へのアクセスをリクエストするためのサポートを追加します。
  • フォアグラウンドの場所または使用中に場所にアクセスするロジックを追加して、Android10および11のアプリにサポートを追加します。

必要なもの

  • コードを実行するためのAndroidStudio3.4以降
  • Android10および11の開発者プレビューを実行しているデバイス/エミュレーター

スタータープロジェクトリポジトリのクローンを作成する

できるだけ早く始めるために、このスタータープロジェクトに基づいて構築することができます。 Gitがインストールされている場合は、次のコマンドを実行するだけです。

 git clone https://github.com/googlecodelabs/while-in-use-location

GitHubページに直接アクセスしてください

Gitがない場合は、プロジェクトをzipファイルとして取得できます。

zipをダウンロード

プロジェクトをインポートする

Android Studioを開き、ようこそ画面から[既存のAndroid Studioプロジェクトを開く]を選択して、プロジェクトディレクトリを開きます。

プロジェクトが読み込まれた後、Gitがすべてのローカル変更を追跡していないというアラートが表示される場合もあります。 [無視]をクリックできます。 (変更をGitリポジトリにプッシュバックすることはありません。)

Androidビューを表示している場合、プロジェクトウィンドウの左上隅に次の画像のようなものが表示されます。 (プロジェクトビューを表示している場合、同じものを表示するにはプロジェクトを展開する必要があります。)

f4b4b6f926c9015b.png

2つのフォルダ( basecomplete )があります。それぞれが「モジュール」として知られています。

Android Studioは、プロジェクトをバックグラウンドで初めてコンパイルするのに数秒かかる場合があることに注意してください。この間、AndroidStudioの下部にあるステータスバーに次のメッセージが表示されます。

4bc64eb3b99eb0ae.png

コードを変更する前に、AndroidStudioがプロジェクトのインデックス作成とビルドを完了するまで待ちます。これにより、AndroidStudioで必要なすべてのコンポーネントを取り込むことができます。

言語の変更を有効にするためにリロードするというプロンプトが表示された場合はどうなりますか?または同様のもので、[はい]を選択します。

スタータープロジェクトを理解する

これで、アプリで場所をリクエストする準備が整いました。開始点としてbaseモジュールを使用します。各ステップで、 baseモジュールにコードを追加します。このコードラボを使い終える頃には、 baseモジュールのコードはcompleteモジュールの内容と一致しているはずです。 completeモジュールは、作業を確認したり、問題が発生した場合に参照したりするために使用できます。

主なコンポーネントは次のとおりです。

  • MainActivity —ユーザーがアプリにデバイスの場所へのアクセスを許可するためのUI
  • LocationService —場所の変更をサブスクライブおよびサブスクライブ解除し、ユーザーがアプリのアクティビティから離れると、フォアグラウンドサービス(通知付き)に昇格するサービス。ここにロケーションコードを追加します。
  • UtilLocationクラスの拡張機能を追加し、 SharedPreferences (簡略化されたデータレイヤー)に場所を保存します。

エミュレーターのセットアップ

Androidエミュレーターのセットアップについては、「エミュレーターで実行」を参照してください。

スタータープロジェクトを実行する

アプリを実行します。

  1. Androidデバイスをコンピューターに接続するか、エミュレーターを起動します。 (デバイスがAndroid 10以降を実行していることを確認してください。)
  2. ツールバーで、ドロップダウンセレクターからbase構成を選択し、[実行]をクリックします

85939bf9e4b61735.png

  1. 次のアプリがデバイスに表示されていることに注意してください。

1b5c527fa8c0e3a7.png

出力画面に位置情報が表示されない場合があります。これは、ロケーションコードをまだ追加していないためです。

コンセプト

このコードラボの焦点は、位置情報の更新を受信し、最終的にAndroid10とAndroid11をサポートする方法を示すことです。

ただし、コーディングを開始する前に、基本を確認することは理にかなっています。

ロケーションアクセスの種類

コードラボの最初から、ロケーションアクセスの4つの異なるオプションを覚えているかもしれません。それらが何を意味するか見てみましょう:

  • アプリの使用中のみ許可する
  • このオプションは、ほとんどのアプリで推奨されるオプションです。 「使用中」または「フォアグラウンドのみ」アクセスとも呼ばれるこのオプションは、Android 10で追加され、アプリがアクティブに使用されている間のみ開発者が場所を取得できるようにします。次のいずれかに該当する場合、アプリはアクティブであると見なされます。
  • アクティビティが表示されます。
  • フォアグラウンドサービスは、継続的な通知とともに実行されています。
  • 一回きり
  • Android 11で追加されました。これは、アプリの使用中にのみ許可するのと同じですが、期間が限られています。詳細については、「 1回限りのアクセス許可」を参照してください。
  • 拒否
  • このオプションは、位置情報へのアクセスを防ぎます。
  • 常に許可する
  • このオプションでは、常に位置情報にアクセスできますが、Android10以降では追加の権限必要です。また、有効なユースケースがあり、 ロケーションポリシーに準拠していることを確認する必要があります。まれなユースケースであるため、このコードラボではこのオプションについては説明しません。ただし、有効なユースケースがあり、バックグラウンドで場所にアクセスするなど、常に場所を適切に処理する方法を理解したい場合は、 LocationUpdatesBackgroundKotlinサンプルを確認してください。

サービス、フォアグラウンドサービス、およびバインディング

アプリの場所の更新を使用しているときにのみ許可を完全にサポートするには、ユーザーがアプリから移動したときを考慮する必要があります。そのような状況で引き続き更新を受信する場合は、フォアグラウンドServiceを作成し、それをNotification関連付ける必要があります。

さらに、同じServiceを使用して、アプリが表示されているときとユーザーがアプリから離れたときに場所の更新をリクエストする場合は、そのServiceをUI要素にバインド/バインド解除する必要があります。

このコードラボが唯一の場所の更新を得ることに焦点を当てているので、あなたはあなたが必要なすべてのコードを見つけることができますForegroundOnlyLocationService.ktクラスを。そのクラスとMainActivity.ktを参照して、それらがどのように連携するかを確認できます。

詳細については、「サービスの概要」および「バインドされたサービスの概要」を参照してください。

権限

NETWORK_PROVIDERまたはGPS_PROVIDERから位置情報の更新を受信するには、AndroidマニフェストファイルでそれぞれACCESS_COARSE_LOCATIONまたはACCESS_FINE_LOCATION権限を宣言して、ユーザーの権限をリクエストする必要があります。これらの権限がないと、アプリは実行時に場所へのアクセスをリクエストできません。

これらの権限は、Android 10以降を実行しているデバイスでアプリを使用する場合に、 1回限り、アプリの使用中にのみ許可することを対象としています。

ロケーション

アプリは、 com.google.android.gms.locationパッケージのクラスを介してサポートされている位置情報サービスのセットにアクセスできます。

主なクラスを見てください:

  • FusedLocationProviderClient
  • これは、ロケーションフレームワークの中心的なコンポーネントです。作成したら、それを使用して場所の更新を要求し、最後の既知の場所を取得します。
  • LocationRequest
  • これは、要求のサービス品質パラメーター(更新、優先順位、および精度の間隔)を含むデータオブジェクトです。これは、場所の更新を要求するときにFusedLocationProviderClientに渡されます。
  • LocationCallback
  • これは、デバイスの場所が変更されたとき、または決定できなくなったときに通知を受信するために使用されます。これには、データベースに保存するLocationを取得できるLocationResultが渡されます。

何をしているのかについての基本的な考え方がわかったので、コードを始めましょう。

このコードラボは、最も一般的な場所のオプションに焦点を当てています。アプリの使用中にのみ許可します。

位置情報の更新を受信するには、アプリに可視アクティビティまたはフォアグラウンドで実行されているサービス(通知付き)が必要です。

権限

このコードラボの目的は、場所の更新を受信する方法を示すことであり、場所のアクセス許可を要求する方法を示すことではないため、アクセス許可ベースのコードは既に作成されています。すでに理解している場合は、スキップしてかまいません。

以下は、権限のハイライトです(この部分にはアクションは必要ありません)。

  1. AndroidManifest.xml使用する権限を宣言します。
  2. 位置情報にアクセスする前に、ユーザーがアプリにアクセスを許可しているかどうかを確認してください。アプリがまだ許可を受け取っていない場合は、アクセスをリクエストしてください。
  3. ユーザーの権限の選択を処理します。 (このコードはMainActivity.ktで確認できます。)

TODO: Step 1.0, Review Permissions AndroidManifest.xmlまたはMainActivity.ktTODO: Step 1.0, Review Permissionsを確認すると、アクセス許可用に記述されたすべてのコードが表示されます。

詳細については、権限の概要を参照してください。

ここで、ロケーションコードの記述を開始します。

場所の更新に必要な主要な変数を確認します

baseモジュールで、 TODO: Step 1.1, Review variables検索しますTODO: Step 1.1, Review variablesTODO: Step 1.1, Review variables

ForegroundOnlyLocationService.ktファイル。

このステップではアクションは必要ありません。場所の更新を受け取るために使用する主要なクラスと変数を理解するには、コメントとともに次のコードブロックを確認する必要があります。

// TODO: Step 1.1, Review variables (no changes).
// FusedLocationProviderClient - Main class for receiving location updates.
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient

// LocationRequest - Requirements for the location updates, i.e., how often you
// should receive updates, the priority, etc.
private lateinit var locationRequest: LocationRequest

// LocationCallback - Called when FusedLocationProviderClient has a new Location.
private lateinit var locationCallback: LocationCallback

// Used only for local storage of the last known location. Usually, this would be saved to your
// database, but because this is a simplified sample without a full database, we only need the
// last location to create a Notification if the user navigates away from the app.
private var currentLocation: Location? = null

FusedLocationProviderClientの初期化を確認します

ではbaseモジュールを検索TODO: Step 1.2, Review the FusedLocationProviderClientForegroundOnlyLocationService.ktファイル。コードは次のようになります。

// TODO: Step 1.2, Review the FusedLocationProviderClient.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)

前のコメントで述べたように、これは場所の更新を取得するためのメインクラスです。変数はすでに初期化されていますが、コードを確認して初期化の方法を理解することが重要です。後でここにコードを追加して、場所の更新をリクエストします。

LocationRequestを初期化します

  1. baseモジュールで、 TODO: Step 1.3, Create a LocationRequest検索します。 TODO: Step 1.3, Create a LocationRequest ForegroundOnlyLocationService.ktファイルにTODO: Step 1.3, Create a LocationRequestTODO: Step 1.3, Create a LocationRequestます。
  2. コメントの後に次のコードを追加します。

LocationRequest初期化コードは、リクエストに必要な追加のサービス品質パラメーター(間隔、最大待機時間、および優先度)を追加します。

// TODO: Step 1.3, Create a LocationRequest.
locationRequest = LocationRequest.create().apply {
   // Sets the desired interval for active location updates. This interval is inexact. You
   // may not receive updates at all if no location sources are available, or you may
   // receive them less frequently than requested. You may also receive updates more
   // frequently than requested if other applications are requesting location at a more
   // frequent interval.
   //
   // IMPORTANT NOTE: Apps running on Android 8.0 and higher devices (regardless of
   // targetSdkVersion) may receive updates less frequently than this interval when the app
   // is no longer in the foreground.
   interval = TimeUnit.SECONDS.toMillis(60)

   // Sets the fastest rate for active location updates. This interval is exact, and your
   // application will never receive updates more frequently than this value.
   fastestInterval = TimeUnit.SECONDS.toMillis(30)

   // Sets the maximum time when batched location updates are delivered. Updates may be
   // delivered sooner than this interval.
   maxWaitTime = TimeUnit.MINUTES.toMillis(2)

   priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
  1. コメントを読んで、それぞれがどのように機能するかを理解してください。

LocationCallbackを初期化します

  1. baseモジュールで、 TODO: Step 1.4, Initialize the LocationCallback検索します。 TODO: Step 1.4, Initialize the LocationCallback ForegroundOnlyLocationService.ktファイルでTODO: Step 1.4, Initialize the LocationCallbackTODO: Step 1.4, Initialize the LocationCallbackます。
  2. コメントの後に次のコードを追加します。
// TODO: Step 1.4, Initialize the LocationCallback.
locationCallback = object : LocationCallback() {
    override fun onLocationResult(locationResult: LocationResult) {
        super.onLocationResult(locationResult)

        // Normally, you want to save a new location to a database. We are simplifying
        // things a bit and just saving it as a local variable, as we only need it again
        // if a Notification is created (when the user navigates away from app).
        currentLocation = locationResult.lastLocation

        // Notify our Activity that a new location was added. Again, if this was a
        // production app, the Activity would be listening for changes to a database
        // with new locations, but we are simplifying things a bit to focus on just
        // learning the location side of things.
        val intent = Intent(ACTION_FOREGROUND_ONLY_LOCATION_BROADCAST)
        intent.putExtra(EXTRA_LOCATION, currentLocation)
        LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)

        // Updates notification content if this service is running as a foreground
        // service.
        if (serviceRunningInForeground) {
            notificationManager.notify(
                NOTIFICATION_ID,
                generateNotification(currentLocation))
        }
    }
}

ここで作成するLocationCallbackは、新しい場所の更新が利用可能になったときにFusedLocationProviderClientが呼び出すコールバックです。

コールバックでは、最初にLocationResultオブジェクトを使用して最新の場所を取得します。その後、ローカルブロードキャストを使用して新しい場所のActivityを通知するか(アクティブな場合)、このサービスがフォアグラウンドServiceとして実行されている場合はNotificationを更新しService

  1. コメントを読んで、各部分の機能を理解してください。

場所の変更を購読する

すべてを初期化したので、更新を受信することをFusedLocationProviderClientする必要があります。

  1. baseモジュールで、 Step 1.5, Subscribe to location changes ForegroundOnlyLocationService.ktファイルのStep 1.5, Subscribe to location changesStep 1.5, Subscribe to location changes ForegroundOnlyLocationService.ktます。
  2. コメントの後に次のコードを追加します。
// TODO: Step 1.5, Subscribe to location changes.
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())

requestLocationUpdates()呼び出しは、 FusedLocationProviderClient場所の更新を受信することをFusedLocationProviderClientます。

おそらく、前に定義したLocationRequestLocationCallbackを認識しているでしょう。これらは、 FusedLocationProviderClientに、要求のサービス品質パラメーターと、更新があったときに呼び出す必要があるものを通知します。最後に、 Looperオブジェクトはコールバックのスレッドを指定します。

また、このコードがtry/catchステートメント内にあることに気付くかもしれません。アプリに位置情報にアクセスする権限がない場合にSecurityExceptionが発生するため、このメソッドにはこのようなブロックが必要です。

場所の変更から退会する

アプリが位置情報にアクセスする必要がなくなったら、位置情報の更新を解除することが重要です。

  1. baseモジュールを検索TODO: Step 1.6, Unsubscribe to location changesForegroundOnlyLocationService.ktファイル。
  2. コメントの後に次のコードを追加します。
// TODO: Step 1.6, Unsubscribe to location changes.
val removeTask = fusedLocationProviderClient.removeLocationUpdates(locationCallback)
removeTask.addOnCompleteListener { task ->
   if (task.isSuccessful) {
       Log.d(TAG, "Location Callback removed.")
       stopSelf()
   } else {
       Log.d(TAG, "Failed to remove Location Callback.")
   }
}

removeLocationUpdates()メソッドは、 LocationCallback更新を受信する必要がなくなったことをFusedLocationProviderClientするタスクを設定します。 addOnCompleteListener()は、完了のコールバックを提供し、 Taskを実行します。

前の手順と同様に、このコードがtry/catchステートメント内にあることに気付いたかもしれません。アプリに位置情報にアクセスする権限がない場合にSecurityExceptionが発生するため、このメソッドにはこのようなブロックが必要です

サブスクライブ/サブスクライブ解除コードを含むメソッドがいつ呼び出されるのか不思議に思うかもしれません。これらは、ユーザーがボタンをタップするとメインクラスでトリガーされます。ご覧になりたい場合は、 MainActivity.ktクラスをご覧ください。

アプリを実行する

Android Studioからアプリを実行し、場所ボタンを試してください。

出力画面に位置情報が表示されます。これはAndroid9用の完全に機能するアプリです。

e2936326fdb29181.png

d803774f3a6c4582.png

このセクションでは、Android10のサポートを追加します。

アプリはすでに場所の変更をサブスクライブしているため、行う作業はそれほど多くありません。

実際、あなたがしなければならないのは、フォアグラウンドサービスがロケーション目的で使用されることを指定することだけです。

ターゲットSDK29

  1. baseモジュールで、 build.gradleファイルでTODO: Step 2.1, Target Android 10 and then Android 11.検索します。
  2. 次の変更を行います。
  3. targetSdkVersion29設定します。

コードは次のようになります。

android {
   // TODO: Step 2.1, Target Android 10 and then Android 11.
   compileSdkVersion 29
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 29
       versionCode 1
       versionName "1.0"
   }
...
}

これを行うと、プロジェクトを同期するように求められます。 [今すぐ同期]をクリックします

294987ef6662d635.png

その後、アプリはAndroid10にほぼ対応できるようになります。

フォアグラウンドサービスタイプの追加

Android 10では、使用中の位置情報へのアクセスが必要な場合は、フォアグラウンドサービスのタイプを含める必要があります。あなたの場合、それは位置情報を取得するために使用されています。

baseモジュールで、 TODO: 2.2, Add foreground service type検索しますAndroidManifest.xmlAndroidManifest.xml TODO: 2.2, Add foreground service typeを追加し、次のコードを<service>要素に追加します。

android:foregroundServiceType="location"

コードは次のようになります。

<application>
   ...

   <!-- Foreground services in Android 10+ require type. -->
   <!-- TODO: 2.2, Add foreground service type. -->
   <service
       android:name="com.example.android.whileinuselocation.ForegroundOnlyLocationService"
       android:enabled="true"
       android:exported="false"
       android:foregroundServiceType="location" />
</application>

それでおしまい!アプリは、Androidでの位置情報のベストプラクティスに従うことで、「使用中」のAndroid10の位置情報をサポートします。

アプリを実行する

Android Studioからアプリを実行し、場所ボタンを試してください。

すべてが以前と同じように機能するはずですが、Android 10で機能するようになりました。以前に場所のアクセス許可を受け入れなかった場合は、アクセス許可画面が表示されます。

75c80ddc7fa6bedc.png

42faef703d42ee28.png

3180a5ba502161ce.png

このセクションでは、Android11をターゲットにしています。

すばらしいニュースですbuild.gradleファイル以外のファイルに変更を加える必要はありません。

ターゲットSDK11

  1. baseモジュールで、 build.gradleファイルでTODO: Step 2.1, Target SDKを検索します。
  2. 次の変更を行います。
  3. compileSdkVersion30
  4. targetSdkVersion30

コードは次のようになります。

android {
   TODO: Step 2.1, Target Android 10 and then Android 11.
   compileSdkVersion 30
   defaultConfig {
       applicationId "com.example.android.whileinuselocation"
       minSdkVersion 26
       targetSdkVersion 30
       versionCode 1
       versionName "1.0"
   }
...
}

これを行うと、プロジェクトを同期するように求められます。 [今すぐ同期]をクリックします

294987ef6662d635.png

その後、アプリはAndroid11に対応します。

アプリを実行する

Android Studioからアプリを実行し、ボタンをクリックしてみてください。

すべてが以前と同じように機能するはずですが、Android 11でも機能するようになりました。以前に場所のアクセス許可を受け入れていなかった場合は、アクセス許可画面が表示されます。

bcfb2062f7e9048d.png

bd4ad0c93fbf488e.png

このコードラボに示されている方法で場所のアクセス許可を確認してリクエストすることで、アプリはデバイスの場所に関するアクセスレベルを正常に追跡できます。

このページには、ロケーション権限に関連するいくつかの主要なベストプラクティスがリストされています。ユーザーのデータを安全に保つ方法の詳細については、アプリの権限のベストプラクティスをご覧ください。

必要な権限のみを要求する

必要な場合にのみ許可を求めてください。例えば:

  • どうしても必要な場合を除いて、アプリの起動時に場所の許可をリクエストしないでください。
  • アプリがAndroid10以降をターゲットにしていて、フォアグラウンドサービスがある場合は、マニフェストでforegroundServiceType"location"を宣言します。
  • ユーザーの場所へのより安全で透過的なアクセスで説明されている有効な使用例がない限り、バックグラウンドの場所のアクセス許可を要求しないでください。

許可が与えられていない場合のグレースフルデグラデーションをサポートする

優れたユーザーエクスペリエンスを維持するには、次の状況を適切に処理できるようにアプリを設計します。

  • アプリは位置情報にアクセスできません。
  • バックグラウンドで実行している場合、アプリは位置情報にアクセスできません。

ベストプラクティスを念頭に置いて、Androidで位置情報の更新を受信する方法を学びました。

もっと詳しく知る