본문 바로가기
Kotlin

Kotlin - 콘텐츠 프로바이더 사용하기

by 봄석 2019. 1. 4.

Kotlin - 콘텐츠 프로바이더 사용하기

콘텐츠 프로바이더 란? 

데이터베이스, 파일, 네트워크의 데이터를 다른 앱에 공유합니다.

콘텐츠 프로바이더란 앱의 데이터 접근을 다른 앱에 허용하는 컴포넌트 입니다.

콘텐츠 프로바이더를 이용해 사진 정보를 얻어오는 방법에 대해 알아보겠습니다.


프로바이더를 이용하여 사진 정보를 가지고 오는 순서는 아래와 같습니다

1) 사진 데이터는 외부 저장소에 저장되어 있으므로 외부 저장소 읽기 권한을 앱에 부여합니다.

2) 외부 저장소 읽기 권한은 위험 권한으로 실행 중에 사용자에게 권한을 허용하도록 합니다.

3) contentResolver 객체를 이용하여 데이터를 Cursor 객체로 가지고옵니다.



위 순서를 기억하며 아래 과정을 진행합니다.

1. 프로바이더로 기기의 사진 경로 얻기

2. 매니페스트에 외부 저장소 읽기 권한 추가

3. 권한 확인

4. 권한 요청 

5. 사용 권한 요청 응답처리

6. 앱 실행



안드로이드의 저장소 종류

- 내부 저장소 : OS가 설치된 영역으로 유저가 접근할 수 없는 시스템 영역입니다.

앱이 사용하는 정보와 데이터베이스가 저장됩니다.

- 외부 저장소: 컴퓨터에 기기를 연결하면 저장소로 인식되며 유저가 사용하는 영역입니다.

사진과 동영상은 외부 저장소에 저장됩니다.



안드로이드의 위험 권한

안드로이드권한은 정상권한과 위험권한으로 나뉘는데 인터넷 권한 같은경우는 정산권한이고 

외부 저장소 읽기 권한등은 위험 권한 입니다.


자주쓰는 위험 권한

권한 그룹

권한 

 STORAGE 

 - READ_EXTERNAL_STORAGE

 - WRITE_EXTERNAL_STORAGE 

 LOCATION

 - ACCESS_FINE_LOCATION

 - ACCESS_COARSE_LOCATION 

 SMS

 - SEND_SMS

 - RECEIVE_SMS 

 CAMERA

 - CAMERA 

안드로이드 6.0 (API 23)부터는 모든 앱은 외부에서 리소스 또는 정보를 사용하는 경우 앱에서

사용자에게 권한을 요청해야 합니다. 매니페스트에 권한을 나열하고 앱을 실행 중에 사용자에게 각 권한을 승인받으면 됩니다.




기기의 사진 정보 얻기

프로바이더를 이용해 정보를 얻으려면 contentResolver 객체를 사용해 데이터를 얻을 수 있습니다. 다음은 외부 저장소에 저장된 모든 사진 최신순으로 정렬하여 Cusor 라는 객체를 얻는 코드입니다. 

//모든 사진 정보 가져오기
val cursor=contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            null,        //가져올 항목 배열
            null,        //조건
            null,  //조건

 

MediaStore.Images.ImageColumns.DATE_TAKEN+"DESC") //찍은 날짜 내림차순



첫 번째 인자는 어떤 데이터를 가져오느냐를 URI 형태로 지정합니다.  사진 정보는 외부 저장소에 저장되어 있기 때문에 외부 저장소에 저장된 데이터를 가리키는 URI인 EXTERNAL_CONTENT_URI를 지정합니다.

두 번째 인자는 어떤 항목의 데이터를 가져올 것인지 String 배열로 지정합니다. 가져올 데이터의 구조를 잘 모른다면 일반적으로 null을 지정합니다. null을 지정하면 모든 항목을 가져옵니다.

세 번째 인자는 데이터를 가져올 조건을 지정할 수 있습니다. 전체 데이터를 가져올 때는 null을 설정합니다.

네 번째 인자는 세 번째 인자와 조합하여 조건을 지정할 때 사용합니다. 사용하지 않는다면 null을 설정합니다.

정렬방법을 지정합니다. 사진이 찍힌 날짜의 내림차순 정렬을 합니다.



권한확인

실행중에 위험 권한이 필요한 작업을 수행할 때마다 권한이 있는지를 확인해야 합니다.

권한은 사용자가 앱 설정에서 언제든지 취소할 수 있기 때문입니다. 


권한이 있는지 확인하려면 ContextCompat.checkSelfPermission() 메소드를 사용합니다.

아래 코드는 앱이 외부 저장소 읽기 권한이 있는지를 확인합니다

//권한이 부여되었는지 확인
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            
            //권한이 허용되지 않음
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                //이전에 이미 권한이 거부되었을 때 설명
                alert(
                    "사진 정보를 얻으려면 외부 저장소 권한이 필수로 필요합니다",
                    "권한이 필요한 이유"
                ) {
                    yesButton {
                        //권한 요청
                        ActivityCompat.requestPermissions(
                            this@MainActivity,
                            arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
                            REQUEST_READ_EXTERNAL_STORAGE
                        )
                    }
                    noButton { }
                }.show()
            } else {
                //권한요청
                ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_READ_EXTERNAL_STORAGE)
            }
 
        } else {
            //권한이 이미 허용됨
            getAllPhotos()

        }


shouldShowRequestPermissionRationale() 메소드는 사용자가 전에 권한 요청을 거부 했었는지를 반환합니다 . true 일경우 거부를 한적이 있는것입니다.

만약 사전에 거부했다면 권한이 왜 필요한 지에 대해서 별도의 메시지를 표시하고 다시 권한을 요청합니다.

만약 권한이 부여되지 않았다면 requestPermissions() 메소드를 사용하여 외부 저장소 읽기 권한을 요청합니다. 마지막 인자인 정수값에는 적당한 정수값을 넣어줍니다.

여기서는 REQUEST_READ_EXTERNAL_STORAGE=1000 을 주었습니다.



사용자 권한 요청 응답처리

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when(requestCode){
            REQUEST_READ_EXTERNAL_STORAGE->{
                if((grantResults.isNotEmpty()&& grantResults[0]==PackageManager.PERMISSION_GRANTED)){
                    //권한 허용됨
                    getAllPhotos()
                }else{
                    //권한 거부
                    toast("권한 거부됨")
                }
                return
            }
        }
    }


위에서 ActivityCompat.requestPermissions() 메소드를 호출하면 그결과는 

onRequestPermissionsResult() 메소드로 에서 처리합니다.


grantResults 배열에는 요청한 권한들의 결과가 전달됩니다. 지금은 하나의 권한만 요청되었 요청했기 때문에 0번 인덱스값만 확인합니다. 권한 승인되면 PERMISSION_GRNATED를 반환하고, 거부되면 PERMISSION_DENIED를 반환합니다.


권한 허용되었을때 처리

private fun getAllPhotos() {
        //모든 사진 정보 가져오기
        val cursor = contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            null,        //가져올 항목 배열
            null,        //조건
            null,  //조건
            MediaStore.Images.ImageColumns.DATE_TAKEN + "DESC"
        ) //찍은 날짜 내림차순
 
        if (cursor != null) {
            while (cursor.moveToNext()) { //사진의 경로가 저장된 데이터베이스의 컬럼명은 DATA 상수에 정의되어 있습니다.
                //getColumIndexOrThrow() 메소드를 사용하면 해당 컬럼이 몇 번째 인덱스 인지 알 수 있습니다. getString() 메소드에 그 인덱스를 전달하면 String으로 변환합니다.
                //이것이 uri 즉 사진이 저장된 위치의 경로가 됩니다.
                var uri=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)) 
                Log.d("MainActivity",uri)
            }
            cursor.close()
        }
    }





댓글