Androidアプリで現在地を定期的に取得する

Androidアプリで位置情報を定期的に取得するサンプルアプリを作ってみました。
2020.03.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Androidアプリで位置情報を定期的に取得する事になりましたが、まだまだAndroidアプリ開発に慣れていない身としてはかなり大変でした。 たとえば下記です(心の叫び)。

  • 位置情報のドキュメントがたくさんあって、どれが何で何がどれ?!
  • サンプルコードのその変数、いつどこで定義したなんなの?!?!
  • それはどのファイル(クラス)のどのメソッドに書くんや???

というわけで、いろいろ参考にしつつ試行錯誤しながら作ったサンプルアプリの実装手順を残しておきます。

環境

  • Android Studio 3.6
  • Pixcel 3a (Android 10)

サンプルアプリを作ってみる

プロジェクトの新規作成

Empty Activityを選択して作成します。

Empty Activityでプロジェクトを新規作成する

Google Play Serviceライブラリの追加

app/build.gradlecom.google.android.gms:play-services-location:17.0.0を追加します。

build.gradle

dependencies {
    ...

    implementation 'com.google.android.gms:play-services-location:17.0.0'
}

Google Play Servicesの最新バージョンは下記で確認できます。

パーミッションの付与

AndroidManifest.xmlに下記3つのパーミッションを追加します。

  • ACCESS_COARSE_LOCATION
  • ACCESS_FINE_LOCATION
  • ACCESS_BACKGROUND_LOCATION

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.test.tryforegroundservicesample">

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

    <application
        ...
    </application>
</manifest>

位置情報のパーミッションをリクエストする

ユーザに対して、アプリが位置情報を利用する旨を確認するため、requestPermission()などを追加します。

MainActivity.kt

class MainActivity : AppCompatActivity() {
    companion object {
        private const val PERMISSION_REQUEST_CODE = 1234
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        requestPermission()
    }

    private fun requestPermission() {
        val permissionAccessCoarseLocationApproved =
            ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) ==
                    PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==
                    PackageManager.PERMISSION_GRANTED

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

            if (backgroundLocationPermissionApproved) {
                // フォアグラウンドとバックグランドのバーミッションがある
            } else {
                // フォアグラウンドのみOKなので、バックグラウンドの許可を求める
                ActivityCompat.requestPermissions(this,
                    arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                    PERMISSION_REQUEST_CODE
                )
            }
        } else {
            // 位置情報の権限が無いため、許可を求める
            ActivityCompat.requestPermissions(this,
                arrayOf(
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION
                ),
                PERMISSION_REQUEST_CODE
            )
        }
    }
}

緯度経度を表示するTextViewを定義(ID追加)

標準で生成されたactivity_main.xmlにはIDがないため、@+id/locationTextを追加します。

activity_main.xml

<TextView
    android:id="@+id/locationText"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

位置情報サービス クライアントを作成する

位置情報サービス クライアントとして、FusedLocationProviderClientを追加します。

MainActivity.kt

class MainActivity : AppCompatActivity() {
    companion object {
        private const val PERMISSION_REQUEST_CODE = 1234
    }

    private lateinit var fusedLocationClient: FusedLocationProviderClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        requestPermission()

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
    }

    private fun requestPermission() {
        ...
    }
}

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

定期的に位置情報を受け取るコールバックを実装します。この処理の中で、画面のTextViewを更新します。

MainActivity.kt

class MainActivity : AppCompatActivity() {
    companion object {
        private const val PERMISSION_REQUEST_CODE = 1234
    }

    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private lateinit var locationCallback: LocationCallback

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        requestPermission()

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

        var updatedCount = 0
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult?) {
                locationResult ?: return
                for (location in locationResult.locations){
                    updatedCount++
                    locationText.text = "[${updatedCount}] ${location.latitude} , ${location.longitude}"
                }
            }
        }
    }

    private fun requestPermission() {
        ...
    }
}

位置情報取得の開始と終了を実装する

次の3つを実装します。

  • startLocationUpdates()
  • stopLocationUpdates()
  • createLocationRequest()

これらをonResume()onPause()から呼び出します。

MainActivity.kt

class MainActivity : AppCompatActivity() {
    ...

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

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

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

    private fun requestPermission() {
        ...
    }

    private fun startLocationUpdates() {
        val locationRequest = createLocationRequest() ?: return
        fusedLocationClient.requestLocationUpdates(
            locationRequest,
            locationCallback,
            null)
    }

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

    private fun createLocationRequest(): LocationRequest? {
        return LocationRequest.create()?.apply {
            interval = 10000
            fastestInterval = 5000
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        }
    }
}

動作確認

アプリをビルドして起動します。起動すると位置情報を取得するか聞かれるため、「常に許可」を選択します。

アプリで位置情報へのアクセスを許可する

位置情報(緯度・経度)がバッチリ表示されました!! 最初の数字[3]は、更新した回数です。

位置情報が表示されている様子

さいごに

公式ドキュメントを参考にしながら、位置情報を定期取得するサンプルアプリを作ってみました。 どなたかの参考になれば幸いです。

参考