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
파라미터 설명
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
콜백 메소드를 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개의 댓글