안드로이드 런타임 퍼미션 예제에 오신 것을 환영합니다. 안드로이드 6.0 마시멜로의 도입으로 Google은 앱에서 퍼미션을 처리하는 방식을 변경했습니다. 이 튜토리얼에서는 새로 도입된 안드로이드 런타임 퍼미션에 대해 살펴보고 이를 처리하는 방법을 알아보겠습니다. 제대로 처리되지 않으면 애플리케이션 충돌이 발생할 수 있습니다.
안드로이드 런타임 퍼미션이란 무엇인가요?
안드로이드 6.0 (SDK 23)의 도입으로, 사용자는 필요할 때 특정 권한에 대해 런타임에서 확인을 받게 됩니다. 그래서 우리 마음속에 먼저 드는 질문은 – 오래된 앱은 안드로이드 마시멜로에서 실행될까요? 답은 예입니다. targetSdkVersion이 22 이하인 경우입니다. 따라서 안드로이드 런타임 퍼미션은 하위 호환성을 지원합니다. 하지만 이는 sdk 버전을 22로 설정하여 이전 모델의 퍼미션을 사용할 수 있다는 것을 의미하지는 않습니다. 마시멜로를 사용하는 사용자는 설정-앱-퍼미션에서 위험한 퍼미션(나중에 위험한과 일반적인 퍼미션에 대해 논의할 것입니다)을 취소할 수 있습니다. 사용자가 아직 허용하지 않은 퍼미션을 필요로하는 기능을 호출하려고 하는 경우, 해당 기능은 갑자기 예외(java.lang.SecurityException
)를 throw하여 애플리케이션 충돌로 이어질 것입니다. 따라서 우리 애플리케이션에 이 새로운 안드로이드 퍼미션 모델을 구현해야 합니다.
위험한 및 일반적인 안드로이드 권한
안드로이드는 일부 권한을 위험하게, 일부를 일반적으로 정의합니다. 두 유형 모두에 공통적인 것은 매니페스트 파일에서 정의되어야 한다는 점입니다. Android 6.0부터는 런타임에서는 위험한 권한만 확인하며, 일반 권한은 확인되지 않습니다. 일반 권한의 예는 android.permission.INTERNET
입니다. 위험한 권한은 사용자가 어플리케이션이 무엇을 수행하도록 허용하는지를 이해하기 쉽게 그룹화됩니다. 사용자가 그룹/카테고리 중 하나의 권한을 허용하면 전체 그룹을 허용합니다. 위험한 권한의 예는 android.permission.FINE_LOCATION
및 android.permission.COARSE_LOCATION
입니다. 위치 권한 중 하나를 활성화하면 모든 위치 권한이 활성화됩니다.
Android 런타임 권한 요청
메소드 requestPermissions(String[] permissions, int requestCode);
은 위험한 권한을 요청하는 데 사용되는 공개 메소드입니다. 우리는 권한의 문자열 배열을 전달하여 여러 위험한 권한을 요청할 수 있습니다. 참고: 두 가지 다른 그룹에 속하는 Android 권한은 각각의 다이얼로그를 사용자에게 제공합니다. 그들이 같은 그룹에 속한다면, 한 개의 대화 상자만 표시됩니다. 요청의 결과는 메소드 onRequestPermissionResult
로 전달됩니다. 예시: 앱에서 카메라와 위치에 액세스하려고 합니다. 둘 다 위험한 권한입니다. 애플리케이션이 시작될 때 이러한 권한에 대한 액세스를 요청하는 메시지를 표시합니다. 아래와 같이 권한을 문자열 배열에 추가하고 requestPermissions를 호출합니다:
String[] perms = {"android.permission.FINE_LOCATION", "android.permission.CAMERA"};
int permsRequestCode = 200;
requestPermissions(perms, permsRequestCode);
@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults){
switch(permsRequestCode){
case 200:
boolean locationAccepted = grantResults[0]==PackageManager.PERMISSION_GRANTED;
boolean cameraAccepted = grantResults[1]==PackageManager.PERMISSION_GRANTED;
break;
}
}
이제 사용자가 이미 수락한 권한을 계속 수락하도록 원하지 않습니다. 권한이 이전에 부여되었더라도 사용자가 해당 권한을 나중에 취소하지 않았는지 확인해야 합니다. 이를 위해 모든 권한에 대해 다음 메소드를 호출해야 합니다.
checkSelfPermission(String perm);
이는 PERMISSION_GRANTED 또는 PERMISSION_DENIED의 정수 값을 반환합니다. 참고: 사용자가 앱에서 중요한 권한을 거부하면 shouldShowRequestPermissionRationale(String permission);
를 사용하여 사용자에게 권한 필요성을 설명합니다. 이미 권한이 있는지 확인하는 애플리케이션을 개발해 보겠습니다. 그렇지 않으면, 실행 중에 권한을 요청합니다.
Android Runtime Permissions 프로젝트 구조
Android Runtime Permissions 코드
content_main.xml
에는 두 가지 버튼이 포함되어 있어 권한을 확인하고 요청하는 데 사용됩니다.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.journaldev.runtimepermissions.MainActivity"
tools:showIn="@layout/activity_main">
<Button
android:id="@+id/check_permission"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="wrap_content"
android:text="Check Permission"/>
<Button
android:id="@+id/request_permission"
android:layout_below="@+id/check_permission"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Request Permission"/>
</RelativeLayout>
MainActivity.java
는 다음과 같이 정의되어 있습니다.
package com.journaldev.runtimepermissions;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.CAMERA;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int PERMISSION_REQUEST_CODE = 200;
private View view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Button check_permission = (Button) findViewById(R.id.check_permission);
Button request_permission = (Button) findViewById(R.id.request_permission);
check_permission.setOnClickListener(this);
request_permission.setOnClickListener(this);
}
@Override
public void onClick(View v) {
view = v;
int id = v.getId();
switch (id) {
case R.id.check_permission:
if (checkPermission()) {
Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();
} else {
Snackbar.make(view, "Please request permission.", Snackbar.LENGTH_LONG).show();
}
break;
case R.id.request_permission:
if (!checkPermission()) {
requestPermission();
} else {
Snackbar.make(view, "Permission already granted.", Snackbar.LENGTH_LONG).show();
}
break;
}
}
private boolean checkPermission() {
int result = ContextCompat.checkSelfPermission(getApplicationContext(), ACCESS_FINE_LOCATION);
int result1 = ContextCompat.checkSelfPermission(getApplicationContext(), CAMERA);
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
}
private void requestPermission() {
ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0) {
boolean locationAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean cameraAccepted = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (locationAccepted && cameraAccepted)
Snackbar.make(view, "Permission Granted, Now you can access location data and camera.", Snackbar.LENGTH_LONG).show();
else {
Snackbar.make(view, "Permission Denied, You cannot access location data and camera.", Snackbar.LENGTH_LONG).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)) {
showMessageOKCancel("You need to allow access to both the permissions",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{ACCESS_FINE_LOCATION, CAMERA},
PERMISSION_REQUEST_CODE);
}
}
});
return;
}
}
}
}
break;
}
}
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
}
참고: 런타임에서 확인할 권한은 Manifest 파일에서 응용 프로그램 태그 위에 추가하세요;
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
위의 코드에서 확인하고 요청하는 두 가지 권한은 CAMERA 및 LOCATION입니다. 정적 권한 풀 클래스 이름을 가져오면 완전히 정규화된 경로 대신 PERMISSION 객체만 작성할 수 있습니다. checkPermission()
은 각 권한에 대해 checkSelfPermission을 호출합니다. requestPermission()
은 ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, CAMERA}, PERMISSION_REQUEST_CODE);
를 호출합니다. onRequestPermissionsResult
은 권한이 부여되었는지 여부를 확인합니다. 코드에서 권한이 모두 부여되지 않은 경우 필수적으로 권한을 요청해야 함을 알리는 경고 대화 상자가 표시됩니다. 이를 위해 shouldShowRequestPermissionRationale(String permission)
이 호출되어 권한이 필요함을 보여주는 경고 대화 상자가 표시됩니다. 권한을 수동으로 설정에서 해제할 수 있습니다. 참고: 런타임 권한 특정 메서드는 API 23 이후에만 사용할 수 있습니다. 따라서 각 메서드에서 다음 조건이 확인됩니다 :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
액션 중인 안드로이드 런타임 권한 예제 애플리케이션의 출력은 아래에 제공됩니다. 이것으로 튜토리얼을 마칩니다. 최종 Android 런타임 권한 프로젝트를 다음 링크에서 다운로드할 수 있습니다.
참조: https://developer.android.com/training/permissions/requesting.html
Source:
https://www.digitalocean.com/community/tutorials/android-runtime-permissions-example