Primary/Android

[Android Fundamentals] Content Provider - 1. Overview

해스끼 2024. 7. 8. 21:36

Concept

Content provider는 앱의 데이터를 캡슐화하고, ``ContentResolver`` 인터페이스를 통해 다른 앱에 제공함으로서 서로 다른 프로세스에서 데이터를 공유할 수 있는 표준 인터페이스의 역할을 한다. 앱 내부에서 사용할 수도 있지만, 데이터를 다른 앱에 제공하기 위해 사용되는 경우가 많다.

 

다른 앱에서는 provider client 객체를 사용하여 content provider에 접근한다. 즉 content provider와 provider client를 통해 일관된 데이터 인터페이스를 유지하는 동시에 데이터를 보호하고 프로세스 간 데이터 교환 작업까지 한번에 처리할 수 있다.

데이터를 다른 앱과 공유해야 할 때만 content provider를 사용하자. 예를 들어 연락처 정보는 많은 앱에서 참조하고 싶어할 수 있으므로 content provider에 저장되어야 한다. 공유할 필요가 없는 데이터는 ``Room``을 사용하여 db에 직접 저장하면 된다.

 

Content provider는 다른 앱에 노출하는 데이터 작업을 추상화함으로서 내부 구현이 바뀌어도 외부에 영향을 주지 않도록 할 수 있다.

이런 경우에는 커스텀 content provider를 정의해야 한다.

  • 내 앱에 커스텀 검색 제안 로직을 구현할 때
  • 내 앱의 위젯에 데이터를 넘겨줄 때
  • 내 앱에서 다른 앱으로 복잡한 데이터를 복사/붙여넣기할 때

``ContentResolver``를 통해 요청이 들어오면, 시스템은 URI를 파싱하여 해당 URI를 처리할 수 있는 앱에 요청을 넘겨준다. Content provider는 시스템이 넘겨준 URI를 원하는 대로 해석할 수 있다. URI를 파싱할 때 ``UriMatcher``를 사용하면 좋다.

 

Content provider의 주요 메서드는 다음과 같다. 생명주기 메서드보단 데이터 관련 메서드가 많다.

  • ``onCreate()``
  • ``query(Uri, String, Bundle, CanellationSignal)``
  • ``insert(Uri, ContentValues)``
  • ``update(Uri, ContentValues, Bundle)``
  • ``delete(Uri, Bundle)``

Overview

Content provider는 앱의 데이터 계층을 다른 API 및 컴포넌트와 연결하는 역할을 한다. Content provider가 사용되는 대표적인 경우는 다음과 같다.

  • 다른 앱에 데이터를 공유할 때 (primary!)
  • 위젯에 데이터를 보낼 때
  • 검색 프레임워크에 커스텀 검색 제안 데이터를 보낼 때 (with ``SearchRecentSuggestionsProvider``)
  • ``AbstractThreadSyncAdapter``를 implement하여 서버와 데이터를 동기화할 때
  • UI에서 ``CursorLoader``를 사용하여 데이터를 로드할 때 (이건 진짜 old한 경우)

Content provider의 장점

Content provider를 사용하면 데이터 접근 권한을 다양하게 설정할 수 있다. 내 앱의 content provider만 데이터에 접근할 수 있게 제한할 수도 있고, 폭넓은 권한을 허용할 수도 있고, 읽기/쓰기 권한을 다르게 부여할 수도 있다.

 

안드로이드 프레임워크에는 음악, 영상, 이미지, 연락처 등의 정보를 관리하는 content provider가 내장되어 있다. 여기에서 목록을 확인할 수 있다. 내장된 provider는 (몇몇 제한이 걸려있긴 하지만) 모든 앱에서 접근할 수 있다.

 

위에서 언급한 ``CursorLoader`` 클래스는 비동기 쿼리를 수행하기 위해 content provider를 사용한다 .

Provider에 접근하기

Content provider의 데이터에 접근하고 싶다면, ``Context``에서 얻을 수 있는 ``ContentResolver`` 객체를 사용하여 클라이언트 역할로 접속해야 한다. ``ContentResolver``가 ``ContentProvider``에서 데이터를 얻어오는 구조이다.

 

Provider는 클라이언트로부터 데이터 요청을 받고, 요청을 처리하여 데이터를 반환한다. ``ContentProvider``와 ``ContentResolver`` 모두 기본적인 CRUD 함수를 제공한다. TMI이지만 두 클래스의 CRUD 함수 이름이 정확히 같다고 한다.

 

매우 old하긴 하지만, ``ContentProvider``에 접근하는 예시 중 하나는 ``Activity``나 ``Fragment``에서 ``CursorLoader``를 사용하여 데이터를 비동기적으로 받아오는 경우이다. Kotlin coroutines를 활용한 패턴의 원조 격이라고 볼 수 있을 듯.

Android에 내장된 provider 중에는 사용자가 키보드에 보관한 단어를 저장하는 User Dictionary Provider가 있다. 대략 이렇게 생겼다.

보면 알겠지만 관계형 DB와 매우 유사하다. 이 provider에 접근하는 코드는 다음과 같다.

// Queries the UserDictionary and returns results
cursor = contentResolver.query(
        UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
        projection,                        // The columns to return for each row
        selectionClause,                   // Selection criteria
        selectionArgs.toTypedArray(),      // Selection criteria
        sortOrder                          // The sort order for the returned rows
)

문법이 거의 ``Words`` 테이블이라고 봐도 될 정도로 유사하다.

Content URI

Content URI는 provider에 저장된 데이터를 표현하는 URI이다. Provider의 이름(authority)과 데이터의 주소(path)로 구성된다. 안드로이드에 내장된 provider의 URI는 위 코드처럼 상수로 미리 정의되어 있지만, 커스텀 provider의 URI는 직접 만들어야 한다.

 

``ContentResolver``는 URI의 authority를 파싱하여 데이터를 가져올 provider를 결정한 후, provider에게 path 부분을 넘긴다. Path를 넘겨받은 ``ContentProvider``는 path를 파싱하여 쿼리 매개변수를 얻는다.

 

위에서 ``Words``에 접근할 때 사용한 URI의 실제 값은 이러하다.

content://user_dictionary/words
  • ``content``: 이 URI가 컨텐츠를 나타내는 URI임을 명시한다.
  • ``user_dictionary``는 provider의 이름(authority)이다.
  • ``words``는 provider에 저장된 데이터 테이블의 이름이다.

이 URI는 매개변수가 없으므로 모든 데이터를 반환한다. 물론 매개변수를 더 붙일 수도 있다. ``_ID``가 4인 데이터를 찾는 URI는 다음과 같다.

val singleUri: Uri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI, 4)

URI를 만들 때에는 ``Uri`` 또는 ``Uri.Builder`` 클래스를, URI에 데이터를 덧붙일 때에는 ``ContentUris`` 클래스를 사용하면 좋다.

참고자료

 

ContentProvider  |  Android Developers

 

developer.android.com

 

 

콘텐츠 제공자  |  Android Developers

콘텐츠 제공자는 구조화된 데이터 세트의 액세스를 관리합니다. 데이터를 캡슐화하고, 데이터 보안을 정의하는 데 필요한 메커니즘을 제공합니다. 콘텐츠 제공자는 한 프로세스의 데이터를 다

developer.android.com