- 최종 수정일자 : 2022-03-14 23:17:18 +0900
안드로이드 권한 요청하기
안드로이드의
Dangerous권한은 API 23(마시멜로우) 이상부터는 런타임 시에 권한을 요청하도록 변경되었으며 API 22 이하는 권한에 상관없이 설치시에 자동으로 부여된다.API 23 이상부터는 사용자가
Dangerous권한이 필요한 기능을 시작하려고 할 때에만 권한을 요청해야하며, 사용자가 필요한 권한을 거부하더라도 다른 기능들까지 사용을 못하게 해서는 안된다.- 스노우 어플에서 권한 거부 시 아래와 같이 보여진다.

권한 요청 Workflow
런타임 권한을 요청할 때에는 아래와 같은 방법으로 요청 및 구현해야 한다.
![workflow-runtime]()
- 매니페스트 파일에 권한을 정의한다.
- 사용자가 권한이 필요한 기능을 시작할 때 앱에서는 이미 권한이 수락되었는지 확인한다.
- 권한이 수락되지 않은 경우 왜 이 권한이 필요한지에 대해 사용자에게 설명한 후 권한 수락 창을 띄운다.
- 설명은 필수가 아니지만, 연구에 따르면 필요한 이유가 설명되어 있을 경우 사용자가 안심하고 수락할 가능성이 높다고 한다.
- 권한이 이미 수락된 경우 해당 기능이 동작할 수 있도록 한다.
- 권한을 거부한 경우 위 스노우 어플처럼 해당 기능만 사용을 중지하고 나머지 기능들은 그대로 둔다.
구현하기
READ_EXTERNAL_STORAGE와WRITE_EXTERNAL_STORAGE퍼미션을 요청하는 코드이다.
1. 매니페스트 파일에 권한을 정의한다.
AndroidManifest.xml
1
2
3
4
5
6
7
<manifest ...>
...
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"/>
...
</manifest>
WRITE_EXTERNAL_STORAGE의 경우 Andoid 9.0 (API LEVEL 28, 파이)까지만 해당 퍼미션을 이용해 권한 요청이 가능하다.- Android 10.0부터는
Scoped Storage가 적용되어WRITE_EXTERNAL_STORAGE의 사용이 불가능하다.- 단, Android 10에서는
requestLegacyExternalStorage를 이용해 저장소에 접근할 수 있다고 한다. - 그러나 11부터는 적용되지 않으므로 주의해야 한다.
- 단, Android 10에서는
2. 권한 관련 확장 함수를 작성한다.
AppCompatActivityExt.kt
1
2
3
4
5
6
7
8
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
fun AppCompatActivity.checkSelfPermissionCompat(permission: String) =
ActivityCompat.checkSelfPermission(this, permission)
fun AppCompatActivity.requestPermissionsCompat(permissionsArray: Array<String>, requestCode: Int) =
ActivityCompat.requestPermissions(this, permissionsArray, requestCode)
- 끝이 Compat으로 끝나는 경우 호환성 이슈가 있는 경우 내부적으로 알아서 처리해준다.
ActivityCompat의requestPermissions함수를 살펴보면 아래와 같다.1 2 3 4 5 6 7 8 9 10
public static void requestPermissions(final @NonNull Activity activity, final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) { [...] if (Build.VERSION.SDK_INT >= 23) { [...] activity.requestPermissions(permissions, requestCode); } else if (activity instanceof OnRequestPermissionsResultCallback) { [...] }
- API Level 23 이상에서 Dangerous 타입의 퍼미션인 경우 런타임 퍼미션으로 실행해야하고 그 이전 버전에서는 런타임 퍼미션이 적용되지 않는다.
- 따라서 내부적으로 위 코드처럼 23버전과 23이전 버전을 나누어 구현되어 있다.
- 사용하려는 함수가 XXXCompat에도 있다면 Compat에 있는 함수를 이용하는게 나을 것 같다.
3. 메인 액티비티를 구현한다.
MainActivity.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import android.Manifest
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.core.app.ActivityCompat
const val READWRITE_EXTERNAL_PERMISSION = 0
class MainActivity : AppCompatActivity(), ActivityCompat.OnRequestPermissionsResultCallback {
private lateinit var tvReadWritePermission: TextView
private lateinit var btnPermissionCheck: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvReadWritePermission = findViewById(R.id.textview_readwrite_permission)
btnPermissionCheck = findViewById(R.id.btn_get_permission)
btnPermissionCheck.setOnClickListener {
checkPermissions()
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
var grantCount = 0
if (requestCode == READWRITE_EXTERNAL_PERMISSION) {
for (result in grantResults) {
if (result == PackageManager.PERMISSION_GRANTED) {
grantCount++
}
}
if (grantCount == 2) {
Toast.makeText(this, "읽기/쓰기 가능", Toast.LENGTH_SHORT).show()
tvReadWritePermission.text = "읽기/쓰기 가능"
} else {
Toast.makeText(this, "읽기/쓰기 불가", Toast.LENGTH_SHORT).show()
tvReadWritePermission.text = "읽기/쓰기 불가"
}
}
}
private fun checkPermissions() {
if (checkSelfPermissionCompat(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
checkSelfPermissionCompat(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
tvReadWritePermission.text = "읽기/쓰기 가능"
} else {
requestPermissions()
}
}
private fun requestPermissions() {
requestPermissionsCompat(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
),
READWRITE_EXTERNAL_PERMISSION
)
}
}
버튼을 클릭하면
checkPermissions함수로 넘어가 위에서 작성한 확장함수인checkSelfPermissionCompat을 이용해 읽기/쓰기에 권한이 이미 있는지 확인하고 없다면requestPermissions함수로 넘어가 확장함수requestPermissionsCompat를 이용해 퍼미션을 요청한다.퍼미션을 수락/거절하게 되면 그 결과가
onRequestPermissionsResult함수로 콜백되어 표현된다.
테스트
API 28에서 테스트
![api28]()
API 31에서 테스트
![api31]()
[참고]
- https://developer.android.com/training/permissions/requesting?hl=ko



Comments powered by Disqus.