- 최종 수정일자 : 2022-03-14 23:17:18 +0900
안드로이드 권한 요청하기
안드로이드의
Dangerous
권한은 API 23(마시멜로우) 이상부터는 런타임 시에 권한을 요청하도록 변경되었으며 API 22 이하는 권한에 상관없이 설치시에 자동으로 부여된다.API 23 이상부터는 사용자가
Dangerous
권한이 필요한 기능을 시작하려고 할 때에만 권한을 요청해야하며, 사용자가 필요한 권한을 거부하더라도 다른 기능들까지 사용을 못하게 해서는 안된다.- 스노우 어플에서 권한 거부 시 아래와 같이 보여진다.
권한 요청 Workflow
런타임 권한을 요청할 때에는 아래와 같은 방법으로 요청 및 구현해야 한다.
- 매니페스트 파일에 권한을 정의한다.
- 사용자가 권한이 필요한 기능을 시작할 때 앱에서는 이미 권한이 수락되었는지 확인한다.
- 권한이 수락되지 않은 경우 왜 이 권한이 필요한지에 대해 사용자에게 설명한 후 권한 수락 창을 띄운다.
- 설명은 필수가 아니지만, 연구에 따르면 필요한 이유가 설명되어 있을 경우 사용자가 안심하고 수락할 가능성이 높다고 한다.
- 권한이 이미 수락된 경우 해당 기능이 동작할 수 있도록 한다.
- 권한을 거부한 경우 위 스노우 어플처럼 해당 기능만 사용을 중지하고 나머지 기능들은 그대로 둔다.
구현하기
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에서 테스트
API 31에서 테스트
[참고]
- https://developer.android.com/training/permissions/requesting?hl=ko
Comments powered by Disqus.