안드로이드 14) ContentProvider

밍나·2022년 1월 22일
0

Android

목록 보기
14/36

Concepts of ContentProvider

1. ContentProvider

  • 앱과 앱간의 데이터 연동을 목적으로 하는 컴포넌트
  • 한 App(B App)이 다른 App(A App)의 data를 이용해야 하는 경우 사용
    • ex) 나의 앱에서 갤러리 앱의 사진 데이터를 이용해야 하는 경우

  • A App에서 Content Provider를 만들고 B App에서는 이 Content Provider를 이용해서 데이터에 access

2. ContentProvider 사용 방법(A App)
1) ContentProvider를 상속받아 Content Provider 클래스 작성

  • 외부앱에서 아래의 함수들을 실행시킬 수 있음
  • 외부에서 delete하면 안되잖아? : delete 함수 내부 구현 안하면 됨
class MyContentProvider: ContentProvider() {

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {    }

    override fun getType(uri: Uri): String? {    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {    }
    
    override fun onCreate(): Boolean {    }

    override fun query(
        uri: Uri, projection: Array<out String>?, selection: String?,
        selectionArgs: Array<out String>?, sortOrder: String?
    ): Cursor? {    }
    
    override fun update(
        uri: Uri, values: ContentValues?, selection: String?, 
        selectionArgs: Array<out String>?
    ): Int {    }
}

2) AndroidManifest.xml 파일에 Content Provider 등록

  • name, authorities 속성 필수
  • authorities 속성으로 content provider 식별
<provider
    android:name=".MyContentProvider"
    android:authorities="com.example.provider"
    android:enabled="true"
    android:exported="true"></provider>

3. ContentProvider 사용 방법(B App)

  • Content Provider를 이용할 때는 인텐트를 발생시키지 않고, 필요한 순간 시스템에서 자동으로 생성한다.
  • Content Provider를 이용하고자 하는 앱은 query(), insert(), delete(), update() 함수만 호출

1) Query Visibility 관련 설정

  • 외부 앱의 Content Provider를 이용하고자 한다면 해당 앱을 이용하기 위한 Query Visibility 관련 설정을 해야한다.
  • Content Provider를 가지고 있는 앱의 패키지명을 태그로 혹은 Content Provider의 authorities 문자열을 태그로 선언
<queries>
    <!-- 둘 중 하나만 선언되어 있으면 된다-->
    <!-- <provider android:authorities="com.example.test.provider" /> -->
    <package android:name="com.example.test_outer" />
</queries>

2) ContentResolver 객체 이용

  • 시스템의 Content Provider를 이용하기 위한 객체가 ContentResolver 객체
  • ContentResolver 객체는 contentResolver 프로퍼티로 획득하여 query(), insert(), update(), delete() 함수를 호출
contentResolver.query(
    Uri.parse("content://com.example.test.provider"),
    null, null, null, null)
  • 첫번째 매개변수(Uri) : 어떤 앱인지에 대한 식별자, Content Provider를 식별하기 위한 객체
content://com.example.test.provider
// content → scheme
// com.example.test.provider → host

Contacts App

1. Contacts App

  • 주소록 앱을 연동하여 주소록 목록 화면을 띄우기(Activity → Intent)
  • 유저가 선택한 사람의 전화번호 혹은 이메일 정보를 획득(ContentProvider)
<uses-permission android:name="android.permission.READ_CONTACTS"/>

1) 주소록 목록 화면을 띄우기

val intent = Intent(Intent.ACTION_PICK, ContactsContract.CommonDateKinds.Phone.CONTENT_URI)
requestActivity.launch(intent)
  • 암시적 인텐트 사용
  • 인텐트 데이터 정보 중 URL : 주소록 앱을 연동하기 위한 URL
    • ContactsContract.Contacts.CONTENT_URI : 모든 사람 출력
    • ContactsContract.CommonDateKinds.Phone.CONTENT_URI : 전화번호가 있는 사람만 출력
    • ContactsContract.CommonDateKinds.Email.CONTENT_URI : 이메일 정보가 있는 사람만 출력

2) 유저가 선택한 사람의 전화번호 혹은 이메일 정보를 획득

val cursor = contentResolver.query(
    it.data!!.data!!,
    arrayOf<String>(ContactsContract.CommonDateKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDateKinds.Phone.NUMBER),
    null,
    null,
    null
)
  • 만약 주소록 앱에서 한 명을 선택했다면 정보가 넘어오는데, 전화번호 자체가 넘어오는 것이 아닌 선택한 사람의 식별자 값이 URL 형태로 넘어옴
  • 식별자를 조건으로 주소록 앱의 Content Provider 이용

1. Gallery App

  • 갤러리 앱의 목록 화면을 띄우고 유저가 선택한 사진을 받아서 서버로 보내거나 화면에 출력

1) 인텐트로 갤러리앱의 이미지 목록 화면을 출력

val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "image/*"
requestActivity.launch(intent)
  • 암시적 인텐트 사용

2) OOM 문제(Out Of Memory Exception) 해결

  • 이미지 데이터의 사이즈가 너무 클 때 발생할 수 있는 문제
  • BitmapFactory.Option 객체의 inSampleSize 값을 지정해 데이터 사이즈를 줄여서 로딩
// 5로 지정시 5분의 1로 줄여서 로딩
val option = BitmapFactory.Options()
option.inSampleSize=5

3) 유저가 선택한 이미지 정보를 획득

var inputStream = contentResolver.openInputStream(it.data!!.data!!)
val bitmap = BitmapFactory.decodeStream(inputStream, null, option)
  • 갤러리 앱에서 사진을 선택했다면 선택한 이미지의 식별자 값이 넘어옴, Provider에서 식별자 값을 이용해서 우리가 원하는 데이터를 획득
  • 갤러리 앱의 ContentProvider가 제공하는 InputStream 객체 획득
  • InputStream 객체에 의해 넘어오는 데이터를 BitmapFactory에 넘겨서 이미지 객체를 만듦

Call App

1. Call App

  • 핸드폰에서 전화를 걸거나 받는 앱
  • 애플리케이션에 전화번호 데이터가 있을 때 그 전화번호로 전화를 걸 수 있도록 연동

1) 퍼미션 필요

<uses-permission android:name="android.permission.CALL_PHONE"/>

2) 퍼미션 허용시

val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:${editView.text}"))
                startActivity(intent)
  • 인텐트 액션 문자열을 Intent.ACTION_CALL로 지정
  • data 정보의 URL은 tel: 으로 선언, data 정보로 전화번호 명시

Camera App

1. Camera App

  • 카메라 앱을 연동해서 사진이나 영상을 촬영함
  • 카메라 앱을 연동해서 사진을 촬영하고 결과를 되돌려 받는 방법은 두가지
    • 사진 데이터 획득 방법
      • 카메라 앱에서 사진을 찍고 사진의 데이터만 넘겨 받는 것
      • 카메라 앱에서는 파일을 저장하지 않음
      • OOM 문제 때문에 사이즈가 작음
    • 파일 공유 방법
      • 카메라 앱 Intent를 발생 시킬 때 file 정보를 넘김
      • 카메라 앱에서는 file에 사진 저장 후 성공/실패 여부만 넘겨 줌
      • 우리 앱에서는 사진이 필요한 경우 file 정보를 읽어들임

1) 사진 데이터 획득 방법

val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE_
requestActivity.launch(intent)
val bitmap = it.data?.getExtras()?.get("data") as Bitmap

2) 파일 공유 방법
① 애플리케이션간 파일 공유를 위해 xml 파일 준비

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="myfiles"
        path="Android/data/com.example.ex1851/files/Pictures"/>
</paths>
  • FileProvider를 이용하려면 공유하고자 하는 파일의 Uri 값을 준비
  • res>xml>file_paths.xml
  • 이름, 파일 경로 정보 등록
<provider
    android:authorities="com.example.ex1851.fileprovider"
    android:name="androidx.core.content.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_path"></meta-data>
</provider>
  • AndroidManifest.xml 파일에 등록
  • 파일 정보를 카메라 앱에 공유시킬 수 있는 provider → 라이브러리에서 제공
  • 라이브러리에서 제공하고 있는 provider로 등록만 하고 xml 파일 정보만 지정하면 됨

② 앱에서 사진을 저장할 파일을 만든다

val file = File.createTempFile(
	"JPEG_${timeStamp}_",
    ".jpg",
    storageDir
)
filePath = file.absolutePath

③ 파일 정보를 포함해서 인텐트를 발생시켜 카메라 앱을 실행시킨다

val uri = FileProvider.getUriForFile(
    this,
    "com.example.ex1851.fileprovider",
    file
)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
fileLauncher.launch(intent)
  • 파일을 공유하기 위한 Uri 객체를 만들고 이 정보를 인텐트의 엑스트라 데이터로 설정

④ 카메라 앱에서 사진 촬영 후 촬영된 사진을 공유된 파일에 저장한다
⑤ 카메라 앱이 종료되면서 성공/실패를 반환하다
⑥ 앱에서 파일을 읽어 카메라 앱이 저장한 사진 데이터를 이용한다

val bitmap = BitmapFactory.decodeFile(filePath, option)
  • 파일 경로를 decodeFile() 함수에 지정하여 Bitmap 객체를 획득
profile
🤗🤗🤗

2개의 댓글

comment-user-thumbnail
2022년 8월 3일

콘텐츠 프로바이더 말고 콘치즈 프로바이더는 없나요?

1개의 답글