1. 시작하기 전에
Android 10과 11에서는 사용자가 앱의 기기 위치 액세스를 더 세밀하게 제어할 수 있습니다.
Android 11에서 실행되는 앱이 위치 액세스를 요청하면 사용자에게 다음 네 가지 옵션이 표시됩니다.
- 항상 허용
- 앱 사용 중에만 허용 (Android 10)
- 한 번만 (Android 11)
- 거부
Android 10

Android 11

이 Codelab에서는 위치 업데이트를 수신하는 방법과 모든 Android 버전(특히 Android 10 및 11)에서 위치를 지원하는 방법을 알아봅니다. Codelab이 끝나면 위치 업데이트를 가져오는 현재 권장사항을 따르는 앱이 완성됩니다.
기본 요건
실습할 내용
- Android의 위치 권장사항을 따릅니다.
- 포그라운드 위치 정보 액세스 권한 처리 (사용자가 앱을 사용하는 동안 앱이 기기 위치에 액세스하도록 요청하는 경우)
- 위치를 구독하고 구독 취소하는 코드를 추가하여 위치 액세스 요청 지원을 추가하도록 기존 앱을 수정합니다.
- 사용 중이거나 포그라운드 위치에서 위치에 액세스하는 로직을 추가하여 Android 10 및 11을 앱에서 지원합니다.
필요한 항목
- 코드를 실행할 Android 스튜디오 3.4 이상
- Android 10 및 11 개발자 프리뷰를 실행하는 기기/에뮬레이터
2. 시작하기
시작 프로젝트 저장소 클론
이 시작 프로젝트를 기반으로 빠르게 시작할 수 있습니다. Git이 설치되어 있으면 다음 명령어를 실행하면 됩니다.
git clone https://github.com/android/codelab-while-in-use-location
GitHub 페이지를 직접 방문하셔도 됩니다.
Git이 없으면 프로젝트를 ZIP 파일로 가져오면 됩니다.
프로젝트 가져오기
Android 스튜디오를 열고 시작 화면에서 'Open an existing Android Studio project'를 선택하고 프로젝트 디렉터리를 엽니다.
프로젝트가 로드된 후 Git이 일부 로컬 변경사항을 추적하지 않는다는 알림이 표시될 수도 있습니다. 무시를 클릭하면 됩니다. 변경사항이 Git 저장소로 다시 푸시되지 않습니다.
Android 뷰에서는 프로젝트 창의 왼쪽 상단에 다음과 같은 이미지가 표시됩니다. Project 뷰에서는 동일한 내용을 보려면 프로젝트를 펼쳐야 합니다.

폴더 두 개 (base 및 complete)가 있습니다. 이들을 각각 '모듈'이라고 합니다.
Android 스튜디오에서 처음으로 프로젝트를 백그라운드에서 컴파일할 때는 몇 초 정도 걸릴 수 있습니다. 이때 Android 스튜디오 하단의 상태 표시줄에 다음 메시지가 표시됩니다.

Android 스튜디오가 프로젝트의 색싱 및 빌드를 완료할 때까지 기다린 후 코드를 변경합니다. 그러면 Android 스튜디오에서 필요한 모든 구성요소를 가져올 수 있습니다.
Reload for language changes to take effect?라는 메시지나 이와 유사한 메시지가 표시되면 Yes를 선택합니다.
시작 프로젝트 이해
설정이 완료되었으며 앱에서 위치를 요청할 준비가 되었습니다. base 모듈을 시작점으로 사용하세요. 각 단계에서 base 모듈에 코드를 추가합니다. 이 Codelab을 완료하면 base 모듈의 코드가 complete 모듈의 콘텐츠와 일치해야 합니다. complete 모듈은 작업을 확인하거나 문제가 발생했을 때 참고하는 용도로 사용할 수 있습니다.
주요 구성요소는 다음과 같습니다.
MainActivity—사용자가 앱이 기기의 위치에 액세스하도록 허용하는 UILocationService: 위치 변경을 구독 및 구독 해제하고 사용자가 앱의 활동에서 벗어나면 자체적으로 포그라운드 서비스 (알림 포함)로 승격되는 서비스입니다. 여기에 위치 코드를 추가합니다.Util-Location클래스의 확장 함수를 추가하고SharedPreferences(간소화된 데이터 영역)에 위치를 저장합니다.
에뮬레이터 설정
Android Emulator 설정에 관한 자세한 내용은 에뮬레이터에서 실행을 참고하세요.
시작 프로젝트 실행
앱을 실행합니다.
- Android 기기를 컴퓨터에 연결하거나 에뮬레이터를 시작합니다. (기기가 Android 10 이상을 실행하는지 확인합니다.)
- 툴바의 드롭다운 선택기에서
base구성을 선택하고 실행을 클릭합니다.

- 기기에 다음 앱이 표시됩니다.

출력 화면에 위치 정보가 표시되지 않을 수 있습니다. 이는 아직 위치 코드를 추가하지 않았기 때문입니다.
3. 위치 추가
개념
이 Codelab의 초점은 위치 업데이트를 수신하고 궁극적으로 Android 10과 Android 11을 지원하는 방법을 보여주는 것입니다.
하지만 코딩을 시작하기 전에 기본사항을 검토하는 것이 좋습니다.
위치 정보 액세스 권한 유형
Codelab 시작 부분에서 위치 액세스에 관한 네 가지 옵션을 살펴봤습니다. 각 상태의 의미는 다음과 같습니다.
- 앱 사용 중에만 허용
- 이 옵션은 대부분의 앱에 권장되는 옵션입니다. '사용 중' 또는 '포그라운드 전용' 액세스라고도 하는 이 옵션은 Android 10에 추가되었으며, 개발자가 앱이 활성 상태로 사용되는 동안에만 위치를 검색할 수 있도록 합니다. 다음 중 하나에 해당하면 앱이 활성 상태인 것으로 간주됩니다.
- 활동이 표시됩니다.
- 포그라운드 서비스가 진행 중인 알림과 함께 실행되고 있습니다.
- 한 번만
- Android 11에 추가되었으며 앱 사용 중에만 허용과 동일하지만 제한된 시간 동안만 허용됩니다. 자세한 내용은 일회성 권한을 참고하세요.
- 거부
- 이 옵션을 사용하면 위치 정보에 액세스할 수 없습니다.
- 항상 허용
- 이 옵션을 사용하면 항상 위치 정보에 액세스할 수 있지만 Android 10 이상에서는 추가 권한이 필요합니다. 또한 유효한 사용 사례가 있어야 하며 위치 정책을 준수해야 합니다. 이 Codelab에서는 이 옵션을 다루지 않습니다. 드문 사용 사례이기 때문입니다. 하지만 유효한 사용 사례가 있고 백그라운드에서 위치에 액세스하는 등 항상 위치를 적절하게 처리하는 방법을 이해하려면 LocationUpdatesBackgroundKotlin 샘플을 검토하세요.
서비스, 포그라운드 서비스, 바인딩
앱 사용 중에만 허용 위치 업데이트를 완전히 지원하려면 사용자가 앱에서 나가는 경우를 고려해야 합니다. 이 경우에도 업데이트를 계속 수신하려면 포그라운드 Service를 만들고 Notification와 연결해야 합니다.
또한 앱이 표시될 때와 사용자가 앱에서 벗어날 때 동일한 Service를 사용하여 위치 업데이트를 요청하려면 해당 Service를 UI 요소에 바인딩/바인딩 해제해야 합니다.
이 Codelab에서는 위치 업데이트를 가져오는 데만 집중하므로 필요한 모든 코드는 ForegroundOnlyLocationService.kt 클래스에서 확인할 수 있습니다. 해당 클래스와 MainActivity.kt를 탐색하여 함께 작동하는 방식을 확인할 수 있습니다.
자세한 내용은 서비스 개요 및 바인드된 서비스 개요를 참고하세요.
권한
NETWORK_PROVIDER 또는 GPS_PROVIDER에서 위치 업데이트를 받으려면 Android manifest 파일에서 각각 ACCESS_COARSE_LOCATION 또는 ACCESS_FINE_LOCATION 권한을 선언하여 사용자의 권한을 요청해야 합니다. 이러한 권한이 없으면 앱이 런타임에 위치 정보 액세스를 요청할 수 없습니다.
이러한 권한은 Android 10 이상을 실행하는 기기에서 앱을 사용할 때 한 번만 허용 및 앱 사용 중에만 허용 사례를 다룹니다.
위치
앱은 com.google.android.gms.location 패키지의 클래스를 통해 지원되는 위치 서비스 세트에 액세스할 수 있습니다.
기본 클래스를 확인합니다.
FusedLocationProviderClient- 위치 프레임워크의 중앙 구성요소입니다. 생성되면 이를 사용하여 위치 업데이트를 요청하고 마지막으로 알려진 위치를 가져옵니다.
LocationRequest- 요청의 서비스 품질 매개변수 (업데이트 간격, 우선순위, 정확도)를 포함하는 데이터 객체입니다. 위치 업데이트를 요청할 때
FusedLocationProviderClient에 전달됩니다. LocationCallback- 기기 위치가 변경되었거나 더 이상 확인할 수 없을 때 알림을 수신하는 데 사용됩니다. 여기에는
LocationResult이 전달되며, 여기서Location을 가져와 데이터베이스에 저장할 수 있습니다.
이제 무엇을 할지 기본적인 아이디어가 생겼으니 코드를 시작해 보세요.
4. 위치 기능 추가
이 Codelab에서는 가장 일반적인 위치 옵션인 앱 사용 중에만 허용에 중점을 둡니다.
위치 업데이트를 수신하려면 앱에 표시되는 활동이 있거나 포그라운드에서 실행되는 서비스 (알림 포함)가 있어야 합니다.
권한
이 Codelab의 목적은 위치 정보 액세스 권한을 요청하는 방법이 아니라 위치 업데이트를 수신하는 방법을 보여주는 것이므로 권한 기반 코드는 이미 작성되어 있습니다. 이미 이해하고 있다면 건너뛰어도 됩니다.
다음은 권한 관련 주요 사항입니다 (이 부분에 대해서는 아무 조치도 필요하지 않음).
AndroidManifest.xml에서 사용하는 권한을 선언합니다.- 위치 정보에 액세스하기 전에 사용자가 앱에 액세스 권한을 부여했는지 확인하세요. 앱이 아직 권한을 받지 못한 경우 액세스를 요청하세요.
- 사용자의 권한 선택을 처리합니다. (이 코드는
MainActivity.kt에서 확인할 수 있습니다.)
AndroidManifest.xml 또는 MainActivity.kt에서 TODO: Step 1.0, Review Permissions를 검색하면 권한에 대해 작성된 모든 코드가 표시됩니다.
자세한 내용은 권한 개요를 참고하세요.
이제 위치 코드를 작성합니다.
위치 업데이트에 필요한 주요 변수 검토
base 모듈에서 다음을 검색합니다.TODO: 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 모듈에서 ForegroundOnlyLocationService.kt 파일의 TODO: Step 1.2, Review the FusedLocationProviderClient을 검색합니다. 코드는 다음과 같이 표시됩니다.
// TODO: Step 1.2, Review the FusedLocationProviderClient.
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
이전 댓글에서 언급했듯이 위치 업데이트를 가져오는 기본 클래스입니다. 변수는 이미 초기화되어 있지만 코드를 검토하여 초기화 방법을 이해하는 것이 중요합니다. 나중에 여기에 코드를 추가하여 위치 업데이트를 요청합니다.
LocationRequest 초기화
base모듈에서ForegroundOnlyLocationService.kt파일의TODO: Step 1.3, Create a LocationRequest을 검색합니다.- 주석 뒤에 다음 코드를 추가합니다.
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
}
- 설명을 읽어 각 항목의 작동 방식을 파악합니다.
LocationCallback 초기화
base모듈에서ForegroundOnlyLocationService.kt파일의TODO: Step 1.4, Initialize the LocationCallback을 검색합니다.- 주석 뒤에 다음 코드를 추가합니다.
// 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를 업데이트합니다.
- 설명을 읽어 각 부분의 역할을 파악합니다.
위치 변경 구독
모든 항목을 초기화했으므로 업데이트를 수신하고 싶다고 FusedLocationProviderClient에 알려야 합니다.
base모듈에서ForegroundOnlyLocationService.kt파일의Step 1.5, Subscribe to location changes을 검색합니다.- 주석 뒤에 다음 코드를 추가합니다.
// TODO: Step 1.5, Subscribe to location changes.
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
requestLocationUpdates() 호출은 FusedLocationProviderClient에 위치 업데이트를 수신하고 싶다고 알립니다.
앞서 정의한 LocationRequest와 LocationCallback를 인식할 수 있습니다. 이를 통해 FusedLocationProviderClient는 요청의 서비스 품질 매개변수와 업데이트가 있을 때 호출해야 하는 항목을 알 수 있습니다. 마지막으로 Looper 객체는 콜백의 스레드를 지정합니다.
이 코드가 try/catch 문 내에 있음을 알 수 있습니다. 이 메서드는 앱에 위치 정보 액세스 권한이 없는 경우 SecurityException가 발생하므로 이러한 차단이 필요합니다.
위치 변경사항 수신 거부
앱이 더 이상 위치 정보에 액세스할 필요가 없는 경우 위치 업데이트를 수신 중지하는 것이 중요합니다.
base모듈에서ForegroundOnlyLocationService.kt파일의TODO: Step 1.6, Unsubscribe to location changes을 검색합니다.- 주석 뒤에 다음 코드를 추가합니다.
// 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() 메서드는 FusedLocationProviderClient에 더 이상 LocationCallback의 위치 업데이트를 수신하지 않음을 알리는 작업을 설정합니다. addOnCompleteListener()는 완료 콜백을 제공하고 Task를 실행합니다.
이전 단계와 마찬가지로 이 코드는 try/catch 문장 내에 있습니다. 이 메서드는 앱에 위치 정보 액세스 권한이 없는 경우 SecurityException이 발생하므로 이러한 차단이 필요합니다.
subscribe/unsubscribe 코드가 포함된 메서드가 언제 호출되는지 궁금할 수 있습니다. 사용자가 버튼을 탭하면 기본 클래스에서 트리거됩니다. 확인하려면 MainActivity.kt 클래스를 참고하세요.
앱 실행
Android 스튜디오에서 앱을 실행하고 위치 버튼을 사용해 보세요.
출력 화면에 위치 정보가 표시됩니다. Android 9용으로 완전히 작동하는 앱입니다.


5. Android 10 지원
이 섹션에서는 Android 10 지원을 추가합니다.
앱이 이미 위치 변경을 구독하고 있으므로 할 일이 많지 않습니다.
사실 포그라운드 서비스가 위치 목적으로 사용된다고만 지정하면 됩니다.
타겟 SDK 29
base모듈에서build.gradle파일의TODO: Step 2.1, Target Android 10 and then Android 11.을 검색합니다.- 다음과 같이 변경합니다.
targetSdkVersion를29로 설정합니다.
코드는 다음과 같이 표시됩니다.
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"
}
...
}
이 작업을 완료하면 프로젝트를 동기화하라는 메시지가 표시됩니다. Sync Now를 클릭합니다.

이 단계를 완료하면 앱이 Android 10을 거의 지원하게 됩니다.
포그라운드 서비스 유형 추가
Android 10에서는 사용 중 위치 액세스가 필요한 경우 포그라운드 서비스 유형을 포함해야 합니다. 이 경우 위치 정보를 가져오는 데 사용됩니다.
base 모듈의 AndroidManifest.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의 위치 권장사항에 따라 '사용 중'에 Android 10 위치를 지원합니다.
앱 실행
Android 스튜디오에서 앱을 실행하고 위치 버튼을 사용해 보세요.
모든 것이 이전과 동일하게 작동하지만 이제 Android 10에서 작동합니다. 이전에 위치 정보 액세스 권한을 수락하지 않은 경우 이제 권한 화면이 표시됩니다.



6. Android 11 지원
이 섹션에서는 Android 11을 타겟팅합니다.
build.gradle 파일을 제외한 파일은 변경하지 않아도 됩니다.
타겟 SDK 11
base모듈의build.gradle파일에서TODO: Step 2.1, Target SDK를 검색합니다.- 다음과 같이 변경합니다.
compileSdkVersion~30targetSdkVersion~30
코드는 다음과 같이 표시됩니다.
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"
}
...
}
이 작업을 완료하면 프로젝트를 동기화하라는 메시지가 표시됩니다. Sync Now를 클릭합니다.

이렇게 하면 앱이 Android 11용으로 준비됩니다.
앱 실행
Android 스튜디오에서 앱을 실행하고 버튼을 클릭해 보세요.
모든 것이 이전과 동일하게 작동하지만 이제 Android 11에서 작동합니다. 이전에 위치 정보 액세스 권한을 수락하지 않은 경우 이제 권한 화면이 표시됩니다.


7. Android의 위치 전략
이 Codelab에 나와 있는 방법으로 위치 정보 액세스 권한을 확인하고 요청하면 앱에서 성공적으로 기기 위치에 관한 액세스 수준을 파악할 수 있습니다.
이 페이지에는 위치 권한과 관련된 몇 가지 주요 권장사항이 나와 있습니다. 사용자 데이터를 안전하게 유지하는 방법에 관한 자세한 내용은 앱 권한 권장사항을 참고하세요.
필요한 권한만 요청
필요할 때만 권한을 요청하세요. 예를 들면 다음과 같습니다.
- 꼭 필요한 경우가 아니면 앱에서 위치 권한을 요청하지 마세요.
- 앱이 Android 10 이상을 타겟팅하고 포그라운드 서비스가 있는 경우 매니페스트에서
"location"의foregroundServiceType을 선언합니다. - 더욱 안전하고 투명한 사용자 위치 액세스에 설명된 유효한 사용 사례가 없는 한 백그라운드 위치 정보 액세스 권한을 요청하지 마세요.
권한이 부여되지 않는 경우 단계적 성능 저하 지원
좋은 사용자 환경을 유지하려면 단계적으로 다음 상황을 처리할 수 있도록 앱을 설계하세요.
- 앱에 위치 정보에 대한 액세스 권한이 없는 경우
- 백그라운드에서 실행될 때 앱에 위치 정보에 대한 액세스 권한이 없는 경우
8. 축하합니다
권장사항을 염두에 두고 Android에서 위치 업데이트를 수신하는 방법을 알아봤습니다.
자세히 알아보기
- 유효한 사용 사례가 있는 경우 백그라운드 위치 사용을 위한 전체 샘플
- 위치 업데이트 요청