이번 포스팅에서는 런타임 퍼미션에 대해 알아보도록 하겠습니다.
들어가기
안드로이드는 카메라나 저장소, 위치 이용와 같이 시스템에 큰 영향을 미칠 수 있는 기능에 대해서는 위험한 권한으로 분류하여 다음과 같이 관리하고 있습니다.
과거에는 앱 설치시 모든 권한을 부여했지만 Android 6.0(API 레벨 23) 이상에서는 앱이 이런 위험한 권한들을 필요로 할 경우 앱 실행중에 권한을 요청할 수 있도록 시스템에 Runtime Permission 을 도입하였습니다. 여기서는 위치 권한을 예로들어 권한을 획득하는 방법을 보여드리겠습니다.
필요한 권한 정의
우선은 AndroidManifest.xml
에서 앱에 필요한 위험한 권한을 추가합니다. 위치에 관련된 권한은 두가지가 있는데 하나는 네트워크만을 이용해서 약 1.6km 이내로 위치를 파악하는 ACCESS_COARSE_LOCATION
과, GPS도 같이 써서 위치를 약 50m 이내로 더 정확하게 파악하는 ACCESS_FINE_LOCATION
이 있습니다.
|
|
요청하는 권한은 한 묶음으로 만들어 다루기 위해 REQUIRED_PERMISSIONS
로 정의합니다.
|
|
권한 확인
권한을 확인하는 함수를 작성합니다. ContextCompat.checkSelfPermission으로 해당 권한이 부여되었는지 확인할 수 있는데요, REQUIRED_PERMISSIONS
에 대해 all을 수행하여 이중 하나라도 권한이 부여되지 않은 경우 isAllPermissionsGranted
가 false
를 반환하도록 했습니다.
|
|
화면 디자인
화면에 퍼미션을 체크하는 버튼을 하나 만듭니다.
|
|
Callback을 이용한 권한 획득
requestDangerousPermissions 작성
버튼에 클릭리스너를 하나 만들어 권한이 부여되어있지 않으면 권한을 요청하는 로직을 구성합니다. 권한 요청에는 ActivityCompat.requestPermissions를 사용하는데, 이 때 request code가 필요하므로 companion object 안에 REQUEST_CODE_PERMISSIONS
도 정의해 줍니다.
|
|
onRequestPermissionsResult
requestDangerousPermissions
에서 권한을 요청한 뒤 유저의 조작이 행해지면 그 결과는 onRequestPermissionsResult 콜백으로 받게 됩니다. onRequestPermissionsResult에서는 requestCode를 확인한 뒤 모든 권한이 취득된 경우, 확인 결과 권한이 취득되지 않았을 경우 shouldShowRequestPermissionRationale를 써서 권한을 재요청할 수 있습니다. 그럼에도 요청을 거부하면 앱은 더이상 권한을 요청할 수 없습니다. 다음 그림에 권한 요청의 순서를 나타내었습니다.
여기에서는 사용자가 모든 권한을 취득했으면 “Permission granted"를 보여주고, 요청을 거부하면 “Permission required to use app!“을 띄운뒤 권한을 다시 요구하도록 했습니다. 여기에서도 요청을 거부하면 마지막으로 Intent를 사용해 유저를 설정화면으로 직접 보내는 openSettings가 실행되도록 했습니다.
|
|
activity:1.2.0 이후의 권한 획득 방법
ActivityResultContract
그런데 구글은 onRequestPermissionsResult
를 androidx.activity:activity:1.2.0-alpha04
부터 deprecated 시키고, 시스템에서 전달된 결과를 처리하기 위해 ActivityResultContract를 새로 만들었습니다. ActivityResultContract를 사용하면 다음과 같이 미리 정의된 계약서를 전달하는것만으로 액티비티에서 결과를 손쉽게 가져올 수 있습니다.
Nested types | |
---|---|
CaptureVideo | An ActivityResultContract to take a video saving it into the provided content-Uri. |
CreateDocument | An ActivityResultContract to prompt the user to select a path for creating a new document, returning the content:Uri of the item that was create. |
GetContent | An ActivityResultContract to prompt the user to pick a piece of content, receiving a content://Uri for that content that allows you to use android.content.ContentResolver.openInputStream to access the raw data. |
GetMultipleContents | An ActivityResultContract to prompt the user to pick one or more a pieces of content, receiving a content://Uri for each piece of content that allows you to use android.content.ContentResolver.openInputStream to access the raw data. |
OpenDocument | An ActivityResultContract to prompt the user to open a document, receiving its contents as a file:/http:/content:Uri. |
OpenDocumentTree | An ActivityResultContract to prompt the user to select a directory, returning the user selection as a Uri. |
OpenMultipleDocuments | An ActivityResultContract to prompt the user to open (possibly multiple) documents, receiving their contents as file:/http:/content:Uris. |
PickContact | An ActivityResultContract to request the user to pick a contact from the contacts app. |
RequestMultiplePermissions | An ActivityResultContract to request permissions |
RequestPermission | An ActivityResultContract to request a permission |
StartActivityForResult | An ActivityResultContract that doesn’t do any type conversion, taking raw Intent as an input and ActivityResult as an output. |
StartIntentSenderForResult | An ActivityResultContract that calls Activity.startIntentSender. |
TakePicture | An ActivityResultContract to take a picture saving it into the provided content-Uri. |
TakePicturePreview | An ActivityResultContract to take small a picture preview, returning it as a Bitmap. |
TakeVideo | An ActivityResultContract to take a video saving it into the provided content-Uri. |
그럼 계약서 기능을 이용하기 위해 gradle에 Dependency를 추가합니다.
|
|
requestMultiplePermissions 작성
여기서는 복수의 권한을 한번에 요청하기 위해 registerForActivityResult 에 RequestMultiplePermissions 계약서를 전달하고 ActivityResultLauncher를 반환받도록 했습니다. 한 개의 권한만을 요청할 경우에는 RequestPermission 을 전달하면 됩니다. 그리고 권한을 승인하였을때, 승인하지 않았을 때, 완전히 거부하였을 때에 대한 분기도 정의하여 줍니다.
|
|
마지막으로 이미 정의된 requestPermissions
는 주석처리하고 requestPermissionLauncher
를 launch로 실행시키면 됩니다. RequestMultiplePermissions
및 RequestPermission
내부의 getSynchronousResult
에는 권한을 체크하는 로직도 포함되어있기 때문에 사용방식에 따라서는 isAllPermissionsGranted
를 생략할 수도 있습니다.
|
|
이렇게 해서 런타임 퍼미션에 대해 알아보았습니다.