Android Runtime Permission을 RxJava로 처리하기

Android 6.0 API 23 Marshmallow 버전 이상 부터는 Danger Level 권한에 대해서 Runtime Permission 요청 / 처리 를 해야합니다.

위험한 권한 및 권한 그룹

권한 그룹 권한
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

권한 사용

예를 들어, SMS메시지를 수신 해야하는 경우 애플리케이션은 다음과 같이 권한을 지정해야합니다. (런타임권한이 나오기 전인 안드로이드 6.0미만 버전에서는 이것으로 충분했습니다.)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    ...
</manifest>

 

권한 여부 확인하기

6.0을 기준으로 상위버전과 하위 버전 호환을 위해 ActivityCompat이 존재합니다.

권환 확인 메소드
ActivityCompat.checkSelfPermission(activity:Activity, permission: String) : Int 메소드를 이용하면 권한여부를 확인할 수 있습니다.

반환값으로는 Boolean 이 아닌 Int가 반환되는데, 권한이 이미 승인된경우 0, 권한이 없거나 거부된경우 -1로 반환됩니다
이미 정의된 리터럴 상수를 이용하여 조건식을 만들 수 있습니다.

권한 허가됨, 값 0 : PackageManager.PERMISSION_GRANTED
권한 거부됨, 값 1 : PackageManager.PERMISSION_DENIED

파라미터 설명

activity : 권한확인을 필요로 하는 액티비티 객체
예) this@MainActivity

permission : 권한 이름, Manifest.permission에 정의 되어있습니다.

런타임에 권한 요청하기

권한요청 메소드

ActivityCompat.requestPermission(activity:Activity, permissions:Array, requestCode:Int) 메소드를 이용하면 쉽게 권한 요청 다이얼로그를 띄울 수 있습니다.

파라미터 설명

activity : 권한을 필요로 하는 액티비티 객체

permissions : 필요한 권한 목록, String 타입의 배열.
예) arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.Manifest.permission.RECEIVE_SMS)

requestCode : 권한 요청을 구분할 수 있는 코드, 보통 개발자가 직접 리터럴 상수를 정의해서 씀.
예) val REQUEST_PERMISSION_CODE = 0

권한 요청 후 콜백 받기

콜백메서드
권한요청하는 requestPermission 메소드 호출시에 OnRequestPermissionsResultCallback 인터페이스를 구현한 액티비티를 파라미터로 넣었다면 Activitiy 내에서

onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray)

콜백 메소드를 override할 수 있습니다.

FragmentActivity부터 OnRequestPermissionsResultCallback이 구현되어있으며, 일반적으로 FragmentActivity를 상속한 AppcompatActivity를 사용하신다면 onRequestPermissionsResult 콜백 이벤트를 받을 수 있습니다.

콜백 파라미터

requestCode : 권한 요청시 쓰인 requestCode가 들어오입니다. 어디서 언제 요청했는지를 이 코드로 구분할 수 있습니다.
permissions : 요청했던 권한 목록
grantResults : 권한 여부 목록

permissions 와 grantResults에서 같은 인덱스로 참조하면서 권한이 허가 되었는지 여부를 확인할 수 있습니다.

소스 코드 전문

class MainActivity:AppCompatActivity(){

    //요청하고 싶은 권한들을 String 타입의 배열로 선언
    private val permissions = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)

    //요청 코드 선언
    private val REQUEST_PERMISSION_CODE = 0

    //Rx이용시 메모리 누수 방지를 위한 Disposable
    private val disposalBag = CompositeDisposable()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        reqPermissions(this, getPermissionList(permissions))
    }

    //권한 목록을 Single<List<String>>로 변환
    private fun getPermissionList(permissions:Array<String>): Single<List<String>> {
        return permissions.toObservable()
                .filter { permission ->
                    PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, permission)
                }.toList()
    }

    private fun reqPermissions(activity: Activity, list: Single<List<String>>) {
        val disposal = list.subscribe { list ->
            if (list.isNotEmpty()) {
                ActivityCompat.requestPermissions(activity, list.toTypedArray(), REQUEST_PERMISSION_CODE)
            } else {
                //이미 이전에 권한을 획득했음.
            }
        }
        disposalBag.add(disposal)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_PERMISSION_CODE) {
            val disposal = Observables.zip(permissions.toObservable(), grantResults.toObservable())
                    .all {
                        it.second == PackageManager.PERMISSION_GRANTED
                    }.subscribe { t1: Boolean, t2: Throwable? ->
                        if (t2 != null || !t1) {
                            //권한 획득 못함
                        } else {
                            //권한 획득
                        }
                    }
            disposalBag.add(disposal)
        }
    }

    //메모리 누수 방지
    override fun onDestroy() {
        super.onDestroy()
        disposalBag.dispose()
    }
}

 

카테고리: 미분류

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.