Callbackから定期的に受け取る位置情報を Kotlin Coroutines Flow
を使って監視する方法です。
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.1"
}
// AndroidStudio上のFlowの警告を抑止
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
freeCompilerArgs = [
'-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi',
'-Xuse-experimental=kotlinx.coroutines.FlowPreview'
]
}
}
位置情報の取得は FusedLocationProviderClient
を使いました。
FusedLocationProviderClient
の位置情報はCallback形式で返ってくるため、取得後に Flow
に渡しています。
class LocationDatasource(private val fusedLocationProviderClient: FusedLocationProviderClient) {
fun observeLocationUpdate(): Flow<Location> = callbackFlow {
val request = LocationRequest().also {
it.interval = 60000
it.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
val callback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
super.onLocationResult(locationResult)
val location = locationResult?.lastLocation ?: return
offer(Location(lat = location.latitude, lng = location.longitude))
}
}
fusedLocationProviderClient.requestLocationUpdates(request, callback, null)
awaitClose {
fusedLolationProviderClient.removeLocationUpdates(callback)
}
}
}
callbackFlow
で全体を囲い、requestLocationUpdates
で位置情報の取得を開始します。ここでは、1分毎に位置情報を取得しています。
利用側で、Corotuinesが破棄されると、 awaitClose
が呼ばれるため、 removeLocationUpdates
を呼ぶことで位置情報の取得が停止します。
続いて、利用側のコードです。
fun startLocationUpdate() {
launch(exceptionHandler) {
val flow = locationRepository.observeLocationUpdate()
flow.collect {
Timber.d("lat : ${it.lat}, lng : ${it.lng}")
}
}
}
coroutineScope
の launch
内で collect
を呼びます。
ここでは、LocationReopository というRepository経由で位置情報を取得する実装にしています。
interface LocationRepository {
fun observeLocationUpdate(): Flow<Location>
}
class LocationRepositoryImpl(
private val locationDatasource: LocationDatasource
) : LocationRepository {
override fun observeLocationUpdate(): Flow<Location> {
return locationDatasource.observeLocationUpdate()
}
}
@Module
class AppModule {
@Provides
fun provideFusedLocationProviderClient(context: Context): FusedLocationProviderClient {
return LocationServices.getFusedLocationProviderClient(context)
}
@Provides
fun provideLocationDatasource(fusedLocationProviderClient: FusedLocationProviderClient): LocationDatasource {
return LocationDatasource(fusedLocationProviderClient)
}
}
以上です。
繰り返し呼ばれるCallbackを継続的に監視できるため、これから利用する機会が増えそうです。
awaitClose
を使ってシンプルにCallbackを止める仕組みが備わっているのも嬉しいですね。