안드로이드 6.0 (Marshmallow) 권한 획득하기!

개발/안드로이드 2017. 8. 4. 20:44
반응형



안드로이드 6.0 (Marshmallow)에서 권한 획득 하는 방법에 대해서 소개하려 합니다.

안드로이드 6.0 은 API Level이 23 이므로 이후 표기 버전은 API 23 이라고 하도록 하겠습니다.


권한 획득이란?

API 22 이하에서는 "AndroidManifest.xml"에 permission을 지정할 수 있었으나 API 23이상에서는 Runtime Permission으로 변경되었습니다.

Permission 이 필요한 시점에 권한을 요청/수락하여 Permission 을 획득하는 방식입니다.



<권한 획득 할때 나오는 팝업>





기본적으로 Permission 종류는 API 23 이전과 거의 동일합니다.

아래는 구글에서 제공하는 권한 및 권한 그룹 표입니다. 

 권한 그룹

 권한

 CALENDAR

READ_CALENDAR
WRITE_CALENDAR

 CAMERA

CAMERA

 CONTACTS

 READ_CONTACTS

WRITE_CONTACTS

GET_ACCOUNTS

 LOCATION

ACCESS_FINE_LOCATION

ACCESS_COARSE_LOCATION

 MICROPHONE

RECORD_AUDIO

 PHONE

READ_PHONE_STATE

CALL_PHONE

READ_CALL_LOG

WRITE_CALL_LOG

ADD_VOICEMAIL

USE_SIP

PROCESS_OUTGOING_CALLS

 SENSORS

BODY_SENSORS

 SMS

SEND_SMS

RECEIVE_SMS

READ_SMS

RECEIVE_WAP_PUSH

RECEIVE_MMS

 STORAGE

READ_EXTERNAL_STORAGE

WRITE_EXTERNAL_STORAGE


<구글에서 제공하는 권한 및 권한 그룹 표>

(https://developer.android.com/guide/topics/security/permissions.html?hl=ko#normal-dangerous)


API 23에서는 권한 그룹이라는게 있습니다.

그룹으로 권한을 획득하게 되면 해당 그룹에 속해있는 권한을 모두 받을 수 있습니다.



권한 획득 주요 API 

권한 획득에 필요한 주요 API는 아래 표와 같습니다. 


API

설명 

 checkSelfPermission

 권한 획득 상태 체크

 shouldShowRequestPermissionRationale

 권한 재요청 여부 확인

 requestPermissions

 권한 요청

 onRequestPermissionsResult

 권한 획득 정보 Callback


권한 API는 코드를 보는게 이해가 빠르므로 간단하게 작성하였습니다.

"아~ 이런게 있구나"라고 생각하시면 되겠습니다.

Read/Write External Storage 권한이 필요한 예제 코드

권한이 필요한 예제코드를 살펴보고 해당권한을 어떻게 얻는지 추후에 살펴 보도록 하겠습니다.


public void startApp()
{
File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String strDir = file.getAbsolutePath();

File oFile = new File(strDir+"/abc.txt");
FileOutputStream oFos;
try
{
oFos = new FileOutputStream(oFile);
oFos.write("abc".getBytes(), 0, 3);
oFos.close();
} catch (IOException e)
{
e.printStackTrace();
}
}

위 코드는 다운로드 폴더에 "abc.txt" 파일을 만들고 "abc" 라는 텍스트를 입력하는 예제 코드입니다.

코드 실행 시 Android Monitor에 아래와 같이 Permission denied Exception을 발생시킵니다.


java.io.FileNotFoundException: /storage/emulated/0/Download/abc.txt (Permission denied)
at java.io.FileOutputStream.open(Native Method)
at java.io.FileOutputStream.<init>(FileOutputStream.java:221)
at java.io.FileOutputStream.<init>(FileOutputStream.java:169)
at twomix.co.kr.runtimepermission.MainActivity.onCreate(MainActivity.java:45)
at android.app.Activity.performCreate(Activity.java:6955)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2927)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3045)
at android.app.ActivityThread.-wrap14(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1642)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)


권한 API 예제 코드

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

m_oMainActivity = this;

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
checkVerify();
}
else
{
startApp();
}
}


if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)

{

    checkVerify();

}

else

{

    startApp();

}


 API 23이상이면 checkVerify() 메소드를 실행하고 API 22 이하이면 startApp() 메소드를 실행합니다.

 실제 앱을 실행하는 코드는 startApp() 에 있습니다.



public void startApp()
{
File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String strDir = file.getAbsolutePath();

File oFile = new File(strDir+"/abc.txt");
FileOutputStream oFos;
try
{
oFos = new FileOutputStream(oFile);
oFos.write("abc".getBytes(), 0, 3);
oFos.close();
} catch (IOException e)
{
e.printStackTrace();
}
}



@TargetApi(Build.VERSION_CODES.M)
public void checkVerify()
{
if (
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
)
{
// Should we show an explanation?
if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE))
{
// ...
}
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE},
1);
}
else
{
startApp();
}
}




if (

checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||

checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED

     )

 

 checkSelfPermission 메소드는 인자에 해당하는 권한 여부를 리턴합니다. 

 현재 코드는 저장소에 Read/Write 권한이 있는지 확인 하는 코드입니다.



if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) 

 

 권한 팝업에서 한번이라도 거부한 경우에 shouldShowRequestPermissionRationale 메소드가 true 를 리턴하게 됩니다. 

 그리고 권한 팝업 요청 시 "다시 묻지 않음" 체크버튼이 나타나게 됩니다. 





 requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                  Manifest.permission.READ_EXTERNAL_STORAGE}, 1);


 권한 팝업을 요청하는 메소드 입니다.

 여러개 권한을 요청해야 할 경우 String[] 로 담아 요청 하시면 됩니다. 위 코드에서는 WRITE_EXTERNAL_STORAGE,  READ_EXTERNAL_STORAGE 두개의 권한을 요청하고 있습니다.




@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);

if (requestCode == 1)
{
if (grantResults.length > 0)
{
for (int i=0; i<grantResults.length; ++i)
{
if (grantResults[i] == PackageManager.PERMISSION_DENIED)
{
// 하나라도 거부한다면.
new AlertDialog.Builder(this).setTitle("알림").setMessage("권한을 허용해주셔야 앱을 이용할 수 있습니다.")
.setPositiveButton("종료", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
m_oMainActivity.finish();
}
}).setNegativeButton("권한 설정", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.parse("package:" + getApplicationContext().getPackageName()));
getApplicationContext().startActivity(intent);
}
}).setCancelable(false).show();

return;
}
}
startApp();
}
}
}


public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)


 onRequestPermissionsResult 메소드는 권한요청 팝업 Callback 메소드 입니다.

 모든 요청 권한에 대한 획득 여부를 확인 후 한개라도 권환 획득 실패 시 팝업을 띄워주고 있는 코드 입니다.



 아래는 onRequestPermissionsResult 메소드 인자 설명입니다.   

 int requestCode

 권한 팝업 결과값

 String[] permissions

 요청 권한 명칭 

 int[] grantResults

 요청 권한 획득 여부






마무리

Target Sdk Version이 API 23 이상이면 필히 구현해야할 코드입니다. 한번 잘 구현해 놓으면 가져다 쓸 수 있으니 잘 이해하셔야 합니다.

전체 소스도 참고하시면 많은 도움이 되실 껍니다. 

좋은 앱 만드세요~!!!   





전체 소스 다운로드

RuntimePermission.zip


반응형
admin