정기 위치 업데이트 수신

지속적으로 위치를 추적하는 앱은 사용자에게 더 관련성이 높은 정보를 제공할 수 있습니다. 예를 들어, 사용자가 걷거나 운전하는 동안 길을 찾아주는 앱 또는 애셋의 위치를 추적하는 앱은 일정한 간격으로 기기의 위치를 찾아야 합니다. 지리적 위치(위도 및 경도)만이 아니라 기기의 방위(수평 이동 방향), 고도 또는 속도와 같은 추가 정보를 사용자에게 제공할 수 있습니다. 이러한 정보와 기타 정보는 통합 위치 정보 제공자에서 검색할 수 있는 Location 객체에서 제공됩니다.

마지막으로 알려진 위치 가져오기의 과정에 표시된 대로 getLastLocation()을 사용하여 기기의 위치를 가져올 수 있지만 보다 직접적인 접근 방식은 통합 위치 정보 제공자에게 정기 업데이트를 요청하는 것입니다. 이러한 요청에 응답하여 API가 정기적으로 Wi-Fi 및 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 */)
    }
    

자바

    @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
                    // ...
                }
            }
        }
    }
    

자바

    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 이상을 타겟팅하는 경우 앱이 백그라운드에 있는 동안 정기 위치 업데이트를 받으려면 앱의 manifest 파일에 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
       )
    }
    

자바

    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);
    }
    

사용자가 시작한 작업 계속하기

앱에서 운전 중에 세부 경로 안내 내비게이션을 사용하거나 달리는 중에 경로를 추적하는 것과 같은 위치별 워크플로우를 제공할 수 있습니다. 사용자가 이러한 유형의 작업을 실행할 때 일반적으로 앱은 사용자가 기기에서 버튼을 누르거나 기기의 디스플레이를 끄는 경우와 같이 백그라운드에 배치된 후 기기의 위치에 액세스해야 합니다.

이 특정 유형의 사용 사례에서 기기 위치 정보 액세스 권한을 유지하려면 앱의 manifest에서 포그라운드 서비스 유형"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
       )
    }
    

자바

    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)
    }
    

자바

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

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

위치 업데이트가 현재 사용 설정되었는지 여부를 추적하려면 부울인 mRequestingLocationUpdates를 사용하세요. 활동의 onResume() 메서드에서 위치 업데이트가 현재 활성 상태인지 확인하고 아닌 경우 위치 업데이트를 활성화하세요.

Kotlin

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

자바

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

활동 상태 저장

기기 구성 변경(예: 화면 방향 또는 언어 변경)으로 인해 현재 활동이 제거될 수 있습니다. 따라서 앱이 활동을 다시 만드는 데 필요한 정보를 저장해야 합니다. 이를 위한 한 가지 방법은 Bundle 객체에 저장된 인스턴스 상태를 사용하는 것입니다.

다음 코드 샘플에서는 활동의 onSaveInstanceState() 콜백을 사용하여 인스턴스 상태를 저장하는 방법을 보여줍니다.

Kotlin

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

자바

    @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()
    }
    

자바

    @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()에서 환경설정을 검색하세요. 환경설정 저장에 대한 자세한 내용은 키-값 세트 저장을 참조하세요.

다음 과정, 위치 주소 표시에서는 지정된 위치의 상세 주소를 표시하는 방법을 보여줍니다.

추가 리소스

자세히 알아보려면 다음 자료를 활용해 주시기 바랍니다.

샘플