연락처 제공업체는 기기의 사용자 데이터 중앙 저장소를 관리하는 강력하고 유연한 Android 구성요소입니다. 연락처 제공자는 기기의 연락처 애플리케이션에 표시되는 데이터 소스이며, 자체 애플리케이션에서 이 데이터에 액세스하고 기기와 온라인 서비스 간에 데이터를 전송할 수도 있습니다. 이 제공업체는 다양한 데이터 소스를 수용하고 사용자별로 최대한 많은 데이터를 관리하려고 하기 때문에 조직이 복잡합니다. 따라서 제공업체의 API에는 데이터 검색과 수정을 모두 용이하게 하는 광범위한 계약 클래스 및 인터페이스가 포함되어 있습니다.
이 가이드에서 설명하는 내용은 다음과 같습니다.
- 기본적인 제공자 구조.
- 제공자에서 데이터를 검색하는 방법.
- 제공자에서 데이터를 수정하는 방법.
- 서버에서 연락처 제공자로 데이터를 동기화하기 위한 동기화 어댑터를 작성하는 방법
이 가이드에서는 독자가 Android 콘텐츠 제공자의 기본 사항을 알고 있는 것으로 간주합니다. Android 콘텐츠 제공자에 관한 자세한 내용은 콘텐츠 제공업체 기본 정보 가이드를 참고하세요.
연락처 제공자의 조직
연락처 제공자는 Android 콘텐츠 제공자 구성요소입니다. 사용자에 관한 세 가지 유형의 데이터를 유지하며, 각 데이터 유형은 제공업체에서 제공하는 테이블에 해당합니다(그림 1 참고).

그림 1. 연락처 제공자 테이블 구조.
이 세 개의 테이블은 일반적으로 자신의 계약 클래스 이름으로 불립니다. 클래스는 테이블에서 사용하는 콘텐츠 URI, 열 이름, 열 값의 상수를 정의합니다.
-
ContactsContract.Contacts
테이블 - 원시 연락처 행의 집계를 기반으로 여러 사람을 나타내는 행입니다.
-
ContactsContract.RawContacts
테이블 - 사용자 계정 및 유형별로 사람의 데이터 요약이 포함된 행입니다.
-
ContactsContract.Data
테이블 - 원시 연락처의 세부정보(예: 이메일 주소, 전화번호)가 포함된 행입니다.
ContactsContract
의 계약 클래스로 표시되는 다른 테이블은 연락처 제공자가 작업을 관리하거나 기기의 연락처 또는 전화 통신 애플리케이션에서 특정 기능을 지원하는 데 사용하는 보조 테이블입니다.
원시 연락처
원시 연락처는 단일 계정 유형 및 계정 이름에서 가져온 사용자의 데이터를 나타냅니다. 연락처 제공자는 한 사람의 데이터 소스로 두 개 이상의 온라인 서비스를 허용하므로 연락처 제공자는 동일한 사람에 대해 여러 개의 원시 연락처를 허용합니다. 또한 여러 원시 연락처를 사용하면 사용자가 동일한 계정 유형의 두 개 이상의 계정에서 사용자의 데이터를 결합할 수 있습니다.
원시 연락처의 대부분의 데이터는 ContactsContract.RawContacts
테이블에 저장되지 않습니다. 대신 ContactsContract.Data
테이블의 하나 이상의 행에 저장됩니다. 각 데이터 행에는 상위 ContactsContract.RawContacts
행의 RawContacts._ID
값을 포함하는 Data.RAW_CONTACT_ID
열이 있습니다.
중요한 원시 연락처 열
ContactsContract.RawContacts
테이블의 중요한 열은 표 1에 나와 있습니다. 표 뒤에 이어지는 참고 사항을 꼭 읽어주세요.
표 1. 중요한 원시 연락처 열.
열 이름 | 사용 | 참고 |
---|---|---|
ACCOUNT_NAME
|
이 원시 연락처의 소스인 계정 유형의 계정 이름입니다.
예를 들어 Google 계정의 계정 이름은 기기 소유자의 Gmail 주소 중 하나입니다. 자세한 내용은 ACCOUNT_TYPE 의 다음 항목을 참고하세요.
|
이 이름의 형식은 각 계정 유형별로 다릅니다. 이메일 주소가 아닐 수도 있습니다. |
ACCOUNT_TYPE
|
이 원시 연락처의 소스인 계정 유형입니다. 예를 들어 Google 계정의 계정 유형은 com.google 입니다. 항상 소유하거나 관리하는 도메인의 도메인 식별자로 계정 유형을 지정합니다. 이렇게 하면 계정 유형이 고유해집니다.
|
연락처 데이터를 제공하는 계정 유형에는 일반적으로 연락처 제공업체와 동기화하는 연결된 동기화 어댑터가 있습니다. |
DELETED
|
원시 연락처의 '삭제됨' 플래그입니다. | 이 플래그를 사용하면 동기화 어댑터가 서버에서 행을 삭제한 후 저장소에서 행을 삭제할 수 있을 때까지 연락처 제공업체가 행을 내부적으로 유지할 수 있습니다. |
참고
다음은 ContactsContract.RawContacts
테이블에 관한 중요한 참고사항입니다.
-
원시 연락처의 이름은
ContactsContract.RawContacts
의 행에 저장되지 않습니다. 대신ContactsContract.Data
테이블의ContactsContract.CommonDataKinds.StructuredName
행에 저장됩니다. 원시 연락처에는ContactsContract.Data
테이블에 이 유형의 행이 하나만 있습니다. -
주의: 원시 연락처 행에서 자체 계정 데이터를 사용하려면 먼저
AccountManager
에 등록해야 합니다. 이렇게 하려면 사용자에게 계정 목록에 계정 유형과 계정 이름을 추가하라는 메시지를 표시합니다. 이렇게 하지 않으면 연락처 제공업체에서 원시 연락처 행을 자동으로 삭제합니다.예를 들어 앱에서
com.example.dataservice
도메인으로 웹 기반 서비스의 연락처 데이터를 유지하려고 하고 서비스의 사용자 계정이becky.sharp@dataservice.example.com
인 경우, 앱에서 원시 연락처 행을 추가하려면 먼저 사용자가 계정 '유형' (com.example.dataservice
) 및 계정 '이름'(becky.smart@dataservice.example.com
)을 추가해야 합니다. 문서에서 사용자에게 이 요구사항을 설명하거나 사용자에게 유형과 이름 또는 둘 다를 추가하라는 메시지를 표시할 수 있습니다. 계정 유형 및 계정 이름은 다음 섹션에서 자세히 설명합니다.
원시 연락처 데이터 소스
원시 연락처의 작동 방식을 이해하려면 기기에 다음과 같은 세 명의 사용자 계정이 정의된 사용자 '에밀리 디킨슨'을 생각해 보세요.
emily.dickinson@gmail.com
emilyd@gmail.com
- 트위터 계정 'belle_of_amherst'
이 사용자는 계정 설정에서 세 가지 계정 모두에 연락처 동기화를 사용 설정했습니다.
Emily Dickinson이 브라우저 창을 열고 emily.dickinson@gmail.com
로 Gmail에 로그인한 후 주소록을 열고 'Thomas Higginson'을 추가한다고 가정해 보겠습니다. 나중에 emilyd@gmail.com
로 Gmail에 로그인하고 '토마스 히긴슨'에게 이메일을 보내면 자동으로 연락처로 추가됩니다. 또한 트위터에서 'colonel_tom' (토마스 히긴슨의 트위터 ID)을 팔로우합니다.
연락처 제공자는 이 작업의 결과로 원시 연락처를 세 개 생성합니다.
-
emily.dickinson@gmail.com
과 연결된 '토마스 히긴슨'의 원시 연락처입니다. 사용자 계정 유형은 Google입니다. -
emilyd@gmail.com
과 연결된 '토마스 히긴슨'의 두 번째 원시 연락처입니다. 사용자 계정 유형은 마찬가지로 Google입니다. 사용자가 다른 사용자 계정에 추가되었기 때문에 이름이 이전 이름과 동일하더라도 두 번째 원시 연락처가 있습니다. - 'belle_of_amherst'와 연결된 'Thomas Higginson'의 세 번째 원시 연락처입니다. 사용자 계정 유형이 트위터입니다.
데이터
앞에서 언급한 대로 원시 연락처의 데이터는 원시 연락처의 _ID
값에 연결된 ContactsContract.Data
행에 저장됩니다. 이렇게 하면 단일 원시 연락처에 이메일 주소나 전화번호와 같은 동일한 유형의 데이터 인스턴스가 여러 개 있을 수 있습니다. 예를 들어 emilyd@gmail.com
의 'Thomas Higginson'(Google 계정 emilyd@gmail.com
와 연결된 Thomas Higginson의 원시 연락처 행)의 집 이메일 주소가 thigg@gmail.com
이고 직장 이메일 주소가 thomas.higginson@gmail.com
인 경우 연락처 제공업체는 두 이메일 주소 행을 저장하고 모두 원시 연락처에 연결합니다.
이 테이블 하나에 여러 가지 유형의 데이터가 저장된다는 점에 유의하세요. 표시 이름, 전화번호, 이메일, 우편 주소, 사진, 웹사이트 세부정보 행은 모두 ContactsContract.Data
테이블에 있습니다. 이를 관리하는 데 도움이 되도록 ContactsContract.Data
테이블에는 설명적인 이름이 지정된 열과 일반 이름이 지정된 열이 있습니다. 설명 이름 열의 콘텐츠는 행의 데이터 유형과 관계없이 동일한 의미를 가지며, 일반 이름 열의 콘텐츠는 데이터 유형에 따라 다른 의미를 갖습니다.
설명이 포함된 열 이름
다음은 설명이 포함된 열 이름의 몇 가지 예입니다.
-
RAW_CONTACT_ID
-
이 데이터의 원시 연락처의
_ID
열 값입니다. -
MIMETYPE
-
이 행에 저장된 데이터 유형으로, 맞춤 MIME 유형으로 표현됩니다. 연락처 제공자는
ContactsContract.CommonDataKinds
의 서브클래스에 정의된 MIME 유형을 사용합니다. 이러한 MIME 유형은 오픈소스이며 연락처 제공자와 호환되는 모든 애플리케이션 또는 동기화 어댑터에서 사용할 수 있습니다. -
IS_PRIMARY
-
이 유형의 데이터 행이 원시 연락처에 두 번 이상 발생할 수 있는 경우
IS_PRIMARY
열은 유형의 기본 데이터가 포함된 데이터 행을 표시합니다. 예를 들어 사용자가 연락처의 전화번호를 길게 누르고 기본값으로 설정을 선택하면 이 번호가 포함된ContactsContract.Data
행의IS_PRIMARY
열이 0이 아닌 값으로 설정됩니다.
일반 열 이름
일반적으로 사용 가능한 DATA1
~DATA15
의 15개 일반 열과 동기화 어댑터에서만 사용해야 하는 SYNC1
~SYNC4
의 4개 일반 열이 있습니다. 일반 열 이름 상수는 행에 포함된 데이터 유형과 관계없이 항상 작동합니다.
DATA1
열에 색인이 생성됩니다. 연락처 제공업체는 항상 제공업체가 쿼리의 가장 빈번한 타겟이 될 것으로 예상하는 데이터에 이 열을 사용합니다. 예를 들어 이메일 행의 이 열에는 실제 이메일 주소가 포함됩니다.
관례에 따라 DATA15
열은 사진 썸네일과 같은 바이너리 대형 객체(BLOB) 데이터를 저장하기 위해 예약되어 있습니다.
유형별 열 이름
특정 유형의 행에 대한 열을 쉽게 사용할 수 있도록 연락처 제공자는 ContactsContract.CommonDataKinds
의 서브클래스에 정의된 유형별 열 이름 상수도 제공합니다. 상수는 동일한 열 이름에 다른 상수 이름을 부여하기 때문에 특정 유형의 행에 있는 데이터에 액세스하는 데 도움이 됩니다.
예를 들어 ContactsContract.CommonDataKinds.Email
클래스는 MIME 유형이 Email.CONTENT_ITEM_TYPE
인 ContactsContract.Data
행의 유형별 열 이름 상수를 정의합니다. 이 클래스에는 이메일 주소 열의 상수 ADDRESS
가 포함되어 있습니다. ADDRESS
의 실제 값은 'data1'이며 이는 열의 일반 이름과 동일합니다.
주의: 제공업체의 사전 정의된 MIME 유형 중 하나가 있는 행을 사용하여 ContactsContract.Data
테이블에 자체 맞춤 데이터를 추가하지 마세요. 그러지 않으면 데이터가 손실되거나 제공업체가 오작동할 수 있습니다. 예를 들어 DATA1
열에 이메일 주소 대신 사용자 이름이 포함된 MIME 유형이 Email.CONTENT_ITEM_TYPE
인 행을 추가해서는 안 됩니다. 행에 자체 맞춤 MIME 유형을 사용하는 경우 원하는 대로 자체 유형별 열 이름을 정의하고 열을 사용할 수 있습니다.
그림 2는 설명 열과 데이터 열이 ContactsContract.Data
행에 표시되는 방식과 유형별 열 이름이 일반 열 이름을 '오버레이'하는 방식을 보여줍니다.

그림 2. 유형별 열 이름과 일반 열 이름.
유형별 열 이름 클래스
표 2는 가장 보편적으로 사용되는 유형별 열 이름 클래스를 나열한 것입니다.
표 2. 유형별 열 이름 클래스
매핑 클래스 | 데이터 유형 | 참고 |
---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
이 데이터 행과 연결된 원시 연락처의 이름 데이터입니다. | 하나의 원시 연락처에는 이러한 행이 딱 하나만 있습니다. |
ContactsContract.CommonDataKinds.Photo |
이 데이터 행과 연결된 원시 연락처의 기본 사진입니다. | 하나의 원시 연락처에는 이러한 행이 딱 하나만 있습니다. |
ContactsContract.CommonDataKinds.Email |
이 데이터 행과 연결된 원시 연락처의 이메일 주소입니다. | 하나의 원시 연락처에는 여러 개의 이메일 주소가 있을 수 있습니다. |
ContactsContract.CommonDataKinds.StructuredPostal |
이 데이터 행과 연결된 원시 연락처의 우편 주소입니다. | 하나의 원시 연락처에는 여러 개의 우편 주소가 있을 수 있습니다. |
ContactsContract.CommonDataKinds.GroupMembership |
원시 연락처를 연락처 제공자의 그룹 중 하나와 연결하는 식별자입니다. | 그룹은 계정 유형과 계정 이름의 선택적 기능입니다. 연락처 그룹 섹션에서 자세히 설명합니다. |
연락처
연락처 제공자는 모든 계정 유형과 계정 이름의 원시 연락처 행을 결합하여 연락처를 만듭니다. 이렇게 하면 사용자가 특정 사용자에 대해 수집한 모든 데이터를 쉽게 표시하고 수정할 수 있습니다. 연락처 제공자는 새 연락처 행 생성 및 원시 연락처와 기존 연락처 행의 집계를 관리합니다. 애플리케이션과 동기화 어댑터 모두 연락처를 추가할 수 없으며 연락처 행의 일부 열은 읽기 전용입니다.
참고: insert()
를 사용하여 연락처 제공업체에 연락처를 추가하려고 하면 UnsupportedOperationException
예외가 발생합니다. '읽기 전용'으로 표시된 열을 업데이트하려고 하면 업데이트가 무시됩니다.
연락처 제공자는 기존 연락처와 일치하지 않는 새 원시 연락처가 추가된 것에 대한 응답으로 새 연락처를 만듭니다. 또한 기존 원시 연락처 데이터가 더 이상 이전에 연결된 연락처와 일치하지 않는 방식으로 변경되는 경우에도 마찬가지입니다. 애플리케이션 또는 동기화 어댑터가 기존 연락처와 일치하는 새 원시 연락처를 만드는 경우 새 원시 연락처가 기존 연락처에 집계됩니다.
연락처 제공자는 Contacts
테이블의 연락처 행 _ID
열을 사용하여 연락처 행을 원시 연락처 행에 연결합니다. 원시 연락처 테이블 ContactsContract.RawContacts
의 CONTACT_ID
열에는 각 원시 연락처 행과 연결된 연락처 행의 _ID
값이 포함됩니다.
ContactsContract.Contacts
테이블에는 연락처 행의 '영구' 링크인 LOOKUP_KEY
열도 있습니다. 연락처 제공업체는 연락처를 자동으로 유지하므로 집계 또는 동기화에 응답하여 연락처 행의 _ID
값을 변경할 수 있습니다. 이 경우에도 연락처의 LOOKUP_KEY
와 결합된 콘텐츠 URI CONTENT_LOOKUP_URI
는 연락처 행을 계속 가리키므로 LOOKUP_KEY
를 사용하여 '즐겨찾는' 연락처 등의 링크를 유지할 수 있습니다. 이 열에는 _ID
열의 형식과 관련 없는 자체 형식이 있습니다.
그림 3은 세 가지 기본 테이블이 서로 연결되는 방식을 나타낸 것입니다.

그림 3. 연락처, 원시 연락처 및 세부 사항 테이블의 관계.
주의: Google Play 스토어에 앱을 게시하거나 앱이 Android 10 (API 수준 29) 이상을 실행하는 기기에 있는 경우 제한된 연락처 데이터 필드 및 메서드는 더 이상 사용되지 않습니다.
언급된 조건에 따라 시스템은 다음 데이터 필드에 기록된 모든 값을 주기적으로 삭제합니다.
-
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED
-
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED
-
ContactsContract.DataUsageStatColumns.LAST_TIME_USED
-
ContactsContract.DataUsageStatColumns.TIMES_USED
위의 데이터 필드를 설정하는 데 사용하는 API도 사용이 중단되었습니다.
또한 다음 필드에서는 이제 자주 사용하는 연락처를 반환하지 않습니다. 이러한 필드 중 일부는 연락처가 특정 데이터 유형에 속하는 경우에만 연락처 순위에 영향을 미칩니다.
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI
-
CONTENT_FILTER_URI
(이메일, 전화, Callable, Contactables 데이터 종류에만 영향을 미침) -
ENTERPRISE_CONTENT_FILTER_URI
(이메일, 전화, 호출 가능 데이터 종류에만 영향을 미침)
앱이 이러한 입력란이나 API에 액세스하거나 업데이트하는 경우 대체 메서드를 사용하세요. 예를 들어 비공개 콘텐츠 제공업체 또는 앱 또는 백엔드 시스템 내에 저장된 기타 데이터를 사용하여 특정 사용 사례를 충족할 수 있습니다.
앱의 기능이 이 변경사항의 영향을 받지 않는지 확인하려면 이러한 데이터 필드를 수동으로 지우면 됩니다. 이렇게 하려면 Android 4.1 (API 수준 16) 이상을 실행하는 기기에서 다음 ADB 명령어를 실행합니다.
adb shell content delete \ --uri content://com.android.contacts/contacts/delete_usage
동기화 어댑터의 데이터
사용자는 기기에 연락처 데이터를 직접 입력하지만, 동기화 어댑터를 통해 웹 서비스에서 연락처 제공자로 데이터가 전송되기도 합니다. 동기화 어댑터는 기기와 서비스 간의 데이터 전송을 자동화합니다. 동기화 어댑터는 시스템의 제어에 따라 백그라운드에서 실행되며 ContentResolver
메서드를 호출하여 데이터를 관리합니다.
Android에서 동기화 어댑터와 함께 작업하는 웹 서비스는 계정 유형으로 식별됩니다. 각 동기화 어댑터는 하나의 계정 유형과 호환되지만 해당 유형의 여러 계정 이름을 지원할 수 있습니다. 계정 유형 및 계정 이름은 원시 연락처 데이터 소스 섹션에 간단히 설명되어 있습니다. 다음 정의는 계정 유형과 이름이 동기화 어댑터 및 서비스와 어떤 관련이 있는지 자세히 설명합니다.
- 계정 유형
-
사용자가 데이터를 저장한 서비스를 식별합니다. 대부분의 경우 사용자는 서비스로 인증해야 합니다. 예를 들어 Google 주소록은
google.com
코드로 식별되는 계정 유형입니다. 이 값은AccountManager
에서 사용하는 계정 유형에 해당합니다. - 계정 이름
- 계정 유형의 특정 계정 또는 로그인을 식별합니다. Google 주소록 계정은 이메일 주소를 계정 이름으로 사용하는 Google 계정과 동일합니다. 다른 서비스에서는 한 단어로 된 사용자 이름이나 숫자 ID를 사용할 수 있습니다.
계정 유형은 고유하지 않아도 됩니다. 사용자는 여러 Google 주소록 계정을 구성하고 데이터를 주소록 제공업체에 다운로드할 수 있습니다. 개인 계정 이름에 하나의 개인 연락처 세트가 있고 직장 계정에 다른 세트가 있는 경우 이러한 상황이 발생할 수 있습니다. 계정 이름은 일반적으로 고유합니다. 이 두 가지를 함께 사용하면 연락처 제공자와 외부 서비스 간의 특정 데이터 흐름을 식별할 수 있습니다.
서비스의 데이터를 연락처 제공업체로 전송하려면 자체 동기화 어댑터를 작성해야 합니다. 자세한 내용은 연락처 제공자 동기화 어댑터 섹션을 참고하세요.
그림 4는 연락처 제공업체가 사용자에 관한 데이터 흐름에 어떻게 적합한지 보여줍니다. '동기화 어댑터'라고 표시된 상자에는 각 어댑터에 계정 유형별로 라벨이 지정됩니다.

그림 4. 연락처 제공자의 데이터 흐름.
필수 권한
연락처 제공자에 액세스하려는 애플리케이션은 다음 권한을 요청해야 합니다.
- 하나 이상의 테이블에 대한 읽기 권한
-
READ_CONTACTS
:AndroidManifest.xml
에서<uses-permission>
요소를<uses-permission android:name="android.permission.READ_CONTACTS">
로 지정하여 지정합니다. - 하나 이상의 테이블에 대한 쓰기 권한
-
WRITE_CONTACTS
:AndroidManifest.xml
에서<uses-permission>
요소를<uses-permission android:name="android.permission.WRITE_CONTACTS">
로 지정하여 지정합니다.
이들 권한은 사용자 프로필 데이터로 확대되지 않습니다. 사용자 프로필 및 필요한 권한은 다음 섹션인 사용자 프로필에서 설명합니다.
사용자의 연락처 데이터는 개인 정보이며 민감하다는 점에 유의하세요. 사용자는 개인 정보 보호에 관해 우려하고 있으므로 애플리케이션에서 자신 또는 연락처에 관한 데이터를 수집하는 것을 원하지 않습니다. 연락처 데이터에 액세스하는 데 권한이 필요한 이유가 명확하지 않으면 사용자는 애플리케이션에 낮은 평점을 주거나 설치를 거부할 수 있습니다.
사용자 프로필
ContactsContract.Contacts
테이블에는 기기 사용자의 프로필 데이터가 포함된 단일 행이 있습니다. 이 데이터는 사용자의 연락처가 아닌 기기의 user
를 설명합니다. 프로필 연락처 행은 프로필을 사용하는 각 시스템의 원시 연락처 행에 연결됩니다.
각 프로필 원시 연락처 행에는 여러 개의 데이터 행이 있을 수 있습니다. 사용자 프로필에 액세스하기 위한 상수는 ContactsContract.Profile
클래스에서 사용할 수 있습니다.
사용자 프로필에 액세스하려면 특수 권한이 필요합니다. 읽기 및 쓰기에 필요한 READ_CONTACTS
및 WRITE_CONTACTS
권한 외에도 사용자 프로필에 액세스하려면 읽기 및 쓰기 액세스에 각각 android.Manifest.permission#READ_PROFILE 및 android.Manifest.permission#WRITE_PROFILE 권한이 필요합니다.
사용자의 프로필은 민감한 정보로 간주해야 합니다. android.Manifest.permission#READ_PROFILE 권한을 사용하면 기기 사용자의 개인 식별 정보에 액세스할 수 있습니다. 애플리케이션 설명에서 사용자 프로필 액세스 권한이 필요한 이유를 사용자에게 알려야 합니다.
사용자의 프로필이 포함된 연락처 행을 검색하려면 ContentResolver.query()
를 호출합니다. 콘텐츠 URI를 CONTENT_URI
로 설정하고 선택 기준을 제공하지 않습니다. 이 콘텐츠 URI를 프로필의 원시 연락처 또는 데이터를 검색하기 위한 기본 URI로 사용할 수도 있습니다. 예를 들어, 이 스니펫은 프로필에 대한 데이터를 검색합니다.
Kotlin
// Sets the columns to retrieve for the user profile projection = arrayOf( ContactsContract.Profile._ID, ContactsContract.Profile.DISPLAY_NAME_PRIMARY, ContactsContract.Profile.LOOKUP_KEY, ContactsContract.Profile.PHOTO_THUMBNAIL_URI ) // Retrieves the profile from the Contacts Provider profileCursor = contentResolver.query( ContactsContract.Profile.CONTENT_URI, projection, null, null, null )
자바
// Sets the columns to retrieve for the user profile projection = new String[] { Profile._ID, Profile.DISPLAY_NAME_PRIMARY, Profile.LOOKUP_KEY, Profile.PHOTO_THUMBNAIL_URI }; // Retrieves the profile from the Contacts Provider profileCursor = getContentResolver().query( Profile.CONTENT_URI, projection , null, null, null);
참고: 연락처 행을 여러 개 가져와 그중 하나가 사용자 프로필인지 확인하려면 행의 IS_USER_PROFILE
열을 테스트하세요. 연락처가 사용자 프로필인 경우 이 열은 '1'로 설정됩니다.
연락처 제공자 메타데이터
연락처 제공자는 저장소의 연락처 데이터 상태를 추적하는 데이터를 관리합니다. 저장소에 관한 이 메타데이터는 원시 연락처, 데이터, 연락처 테이블 행, ContactsContract.Settings
테이블, ContactsContract.SyncState
테이블을 비롯한 다양한 위치에 저장됩니다. 다음 표에는 이러한 각 메타데이터의 효과가 나와 있습니다.
표 3. 연락처 제공자의 메타데이터
표 | 열 | 값 | 의미 |
---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
'0' - 마지막 동기화 이후 변경되지 않았습니다. |
기기에서 변경되었으며 서버에 다시 동기화해야 하는 원시 연락처를 표시합니다. 이 값은 Android 애플리케이션이 행을 업데이트할 때 연락처 제공업체에서 자동으로 설정합니다.
원시 연락처 또는 데이터 표를 수정하는 동기화 어댑터는 항상 사용하는 콘텐츠 URI에 문자열 |
'1' - 마지막 동기화 이후 변경되었으며 서버에 다시 동기화해야 합니다. | |||
ContactsContract.RawContacts |
VERSION |
이 행의 버전 번호. | 연락처 제공업체는 행 또는 관련 데이터가 변경될 때마다 이 값을 자동으로 증분합니다. |
ContactsContract.Data |
DATA_VERSION |
이 행의 버전 번호. | 연락처 제공업체는 데이터 행이 변경될 때마다 이 값을 자동으로 증분합니다. |
ContactsContract.RawContacts |
SOURCE_ID |
이 원시 연락처를 생성된 계정에 고유하게 식별하는 문자열 값입니다. |
동기화 어댑터가 새 원시 연락처를 만들 때 이 열은 원시 연락처의 서버 고유 ID로 설정해야 합니다. Android 애플리케이션이 새 원시 연락처를 만들 때 애플리케이션은 이 열을 비워 두어야 합니다. 이렇게 하면 동기화 어댑터에 서버에서 새 원시 연락처를 만들고 SOURCE_ID 값을 가져와야 한다고 신호를 보냅니다.
특히 소스 ID는 계정 유형마다 고유해야 하며 동기화 전반에서 안정적이어야 합니다.
|
ContactsContract.Groups |
GROUP_VISIBLE |
'0': 이 그룹의 연락처는 Android 애플리케이션 UI에 표시되어서는 안 됩니다. | 이 열은 사용자가 특정 그룹의 연락처를 숨길 수 있는 서버와의 호환성을 위해 제공됩니다. |
'1': 이 그룹의 연락처는 애플리케이션 UI에 표시될 수 있습니다. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
'0' - 이 계정 및 계정 유형의 경우 그룹에 속하지 않는 연락처는 Android 애플리케이션 UI에 표시되지 않습니다. |
기본적으로 원시 연락처 중 그룹에 속하는 연락처가 없으면 연락처가 표시되지 않습니다(원시 연락처의 그룹 멤버십은 ContactsContract.Data 테이블의 하나 이상의 ContactsContract.CommonDataKinds.GroupMembership 행으로 표시됨).
계정 유형 및 계정의 ContactsContract.Settings 테이블 행에서 이 플래그를 설정하면 그룹이 없는 연락처가 표시되도록 강제할 수 있습니다.
이 플래그를 사용하면 그룹을 사용하지 않는 서버의 연락처를 표시할 수 있습니다.
|
'1' - 이 계정 및 계정 유형의 경우 그룹에 속하지 않는 연락처가 애플리케이션 UI에 표시됩니다. | |||
ContactsContract.SyncState |
(모두) | 이 테이블을 사용하여 동기화 어댑터의 메타데이터를 저장합니다. | 이 테이블을 사용하면 동기화 상태와 기타 동기화 관련 데이터를 기기에 영구 저장할 수 있습니다. |
연락처 제공자 액세스
이 섹션에서는 연락처 제공업체의 데이터에 액세스하기 위한 가이드라인을 설명하며 다음 사항에 중점을 둡니다.
- 엔터티 쿼리.
- 일괄 수정.
- 인텐트로 검색 및 수정.
- 데이터 무결성.
동기화 어댑터에서 수정하는 방법은 연락처 제공업체 동기화 어댑터 섹션에서도 자세히 설명합니다.
엔터티 쿼리
연락처 제공자 테이블은 계층 구조로 구성되므로 행과 행에 연결된 모든 '하위' 행을 검색하는 것이 종종 유용합니다. 예를 들어 사용자의 모든 정보를 표시하려면 단일 ContactsContract.Contacts
행의 모든 ContactsContract.RawContacts
행을 검색하거나 단일 ContactsContract.RawContacts
행의 모든 ContactsContract.CommonDataKinds.Email
행을 검색할 수 있습니다. 이를 용이하게 하기 위해 연락처 제공자는 테이블 간의 데이터베이스 조인처럼 작동하는 항목 구성을 제공합니다.
하나의 엔터티는 마치 상위 테이블과 그 하위 테이블에서 가져온 선택된 몇 개의 열로 이루어진 테이블과 같습니다.
항목을 쿼리할 때는 항목에서 사용할 수 있는 열을 기반으로 프로젝션 및 검색 기준을 제공합니다. 결과는 검색된 하위 테이블 행마다 하나의 행이 포함된 Cursor
입니다. 예를 들어 ContactsContract.Contacts.Entity
에서 연락처 이름과 해당 이름의 모든 원시 연락처에 관한 모든 ContactsContract.CommonDataKinds.Email
행을 쿼리하면 ContactsContract.CommonDataKinds.Email
행마다 행 하나가 포함된 Cursor
가 반환됩니다.
엔터티는 쿼리를 단순화합니다. 항목을 사용하면 먼저 상위 테이블을 쿼리하여 ID를 가져온 다음 해당 ID로 하위 테이블을 쿼리하는 대신 연락처 또는 원시 연락처의 모든 연락처 데이터를 한 번에 가져올 수 있습니다. 또한 연락처 제공업체는 단일 트랜잭션에서 항목에 대한 쿼리를 처리하므로 검색된 데이터가 내부적으로 일관됩니다.
참고: 일반적으로 항목에는 상위 및 하위 테이블의 모든 열이 포함되지 않습니다. 항목의 열 이름 상수 목록에 없는 열 이름을 사용하려고 하면 Exception
이 발생합니다.
다음 스니펫은 하나의 연락처에 대해 모든 원시 연락처 행을 검색하는 방법을 보여줍니다. 스니펫은 'main'과 'detail'이라는 두 가지 활동이 있는 더 큰 애플리케이션의 일부입니다. 기본 활동에는 연락처 행 목록이 표시됩니다. 사용자가 연락처 행을 선택하면 활동은 세부정보 활동에 ID를 전송합니다. 세부정보 활동은 ContactsContract.Contacts.Entity
를 사용하여 선택한 연락처와 연결된 모든 원시 연락처의 모든 데이터 행을 표시합니다.
다음 스니펫은 '세부정보' 활동에서 가져온 것입니다.
Kotlin
... /* * Appends the entity path to the URI. In the case of the Contacts Provider, the * expected URI is content://com.google.contacts/#/entity (# is the ID value). */ contactUri = Uri.withAppendedPath( contactUri, ContactsContract.Contacts.Entity.CONTENT_DIRECTORY ) // Initializes the loader identified by LOADER_ID. loaderManager.initLoader( LOADER_ID, // The identifier of the loader to initialize null, // Arguments for the loader (in this case, none) this // The context of the activity ) // Creates a new cursor adapter to attach to the list view cursorAdapter = SimpleCursorAdapter( this, // the context of the activity R.layout.detail_list_item, // the view item containing the detail widgets mCursor, // the backing cursor fromColumns, // the columns in the cursor that provide the data toViews, // the views in the view item that display the data 0) // flags // Sets the ListView's backing adapter. rawContactList.adapter = cursorAdapter ... override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { /* * Sets the columns to retrieve. * RAW_CONTACT_ID is included to identify the raw contact associated with the data row. * DATA1 contains the first column in the data row (usually the most important one). * MIMETYPE indicates the type of data in the data row. */ val projection: Array<String> = arrayOf( ContactsContract.Contacts.Entity.RAW_CONTACT_ID, ContactsContract.Contacts.Entity.DATA1, ContactsContract.Contacts.Entity.MIMETYPE ) /* * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw * contact collated together. */ val sortOrder = "${ContactsContract.Contacts.Entity.RAW_CONTACT_ID} ASC" /* * Returns a new CursorLoader. The arguments are similar to * ContentResolver.query(), except for the Context argument, which supplies the location of * the ContentResolver to use. */ return CursorLoader( applicationContext, // The activity's context contactUri, // The entity content URI for a single contact projection, // The columns to retrieve null, // Retrieve all the raw contacts and their data rows. null, // sortOrder // Sort by the raw contact ID. ) }
자바
... /* * Appends the entity path to the URI. In the case of the Contacts Provider, the * expected URI is content://com.google.contacts/#/entity (# is the ID value). */ contactUri = Uri.withAppendedPath( contactUri, ContactsContract.Contacts.Entity.CONTENT_DIRECTORY); // Initializes the loader identified by LOADER_ID. getLoaderManager().initLoader( LOADER_ID, // The identifier of the loader to initialize null, // Arguments for the loader (in this case, none) this); // The context of the activity // Creates a new cursor adapter to attach to the list view cursorAdapter = new SimpleCursorAdapter( this, // the context of the activity R.layout.detail_list_item, // the view item containing the detail widgets mCursor, // the backing cursor fromColumns, // the columns in the cursor that provide the data toViews, // the views in the view item that display the data 0); // flags // Sets the ListView's backing adapter. rawContactList.setAdapter(cursorAdapter); ... @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { /* * Sets the columns to retrieve. * RAW_CONTACT_ID is included to identify the raw contact associated with the data row. * DATA1 contains the first column in the data row (usually the most important one). * MIMETYPE indicates the type of data in the data row. */ String[] projection = { ContactsContract.Contacts.Entity.RAW_CONTACT_ID, ContactsContract.Contacts.Entity.DATA1, ContactsContract.Contacts.Entity.MIMETYPE }; /* * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw * contact collated together. */ String sortOrder = ContactsContract.Contacts.Entity.RAW_CONTACT_ID + " ASC"; /* * Returns a new CursorLoader. The arguments are similar to * ContentResolver.query(), except for the Context argument, which supplies the location of * the ContentResolver to use. */ return new CursorLoader( getApplicationContext(), // The activity's context contactUri, // The entity content URI for a single contact projection, // The columns to retrieve null, // Retrieve all the raw contacts and their data rows. null, // sortOrder); // Sort by the raw contact ID. }
로드가 완료되면 LoaderManager
이 onLoadFinished()
콜백을 호출합니다. 이 메서드에 전달되는 인수 중 하나는 쿼리 결과가 포함된 Cursor
입니다. 자체 앱에서 이 Cursor
에서 데이터를 가져와 표시하거나 추가로 작업할 수 있습니다.
일괄 수정
가능하면 ContentProviderOperation
객체의 ArrayList
를 만들고 applyBatch()
를 호출하여 '일괄 모드'로 연락처 제공업체에 데이터를 삽입, 업데이트, 삭제해야 합니다. 연락처 제공업체는 단일 트랜잭션에서 applyBatch()
의 모든 작업을 실행하므로 수정사항으로 인해 연락처 저장소가 일치하지 않는 상태로 남아 있는 일은 없습니다. 일괄 수정은 원시 연락처와 세부정보 데이터를 동시에 삽입하는 것도 용이하게 합니다.
참고: 단일 원시 연락처를 수정하려면 앱에서 수정을 처리하는 대신 기기의 연락처 애플리케이션에 인텐트를 전송하는 것이 좋습니다. 이렇게 하는 방법은 인텐트를 사용한 검색 및 수정 섹션에 자세히 설명되어 있습니다.
양보 지점
많은 작업이 포함된 일괄 수정은 다른 프로세스를 차단하여 전반적인 사용자 환경이 저하될 수 있습니다. 실행하려는 모든 수정사항을 최대한 적은 개수의 별도 목록으로 정리하고 동시에 시스템을 차단하지 않도록 하려면 하나 이상의 작업에 수익률 포인트를 설정해야 합니다.
포인트는 isYieldAllowed()
값이 true
로 설정된 ContentProviderOperation
객체입니다. 연락처 제공업체가 포기 지점을 만나면 다른 프로세스가 실행되도록 작업을 일시중지하고 현재 트랜잭션을 닫습니다. 제공업체가 다시 시작되면 ArrayList
의 다음 작업을 계속하고 새 트랜잭션을 시작합니다.
수익 포인트는 applyBatch()
호출당 두 개 이상의 트랜잭션을 초래합니다. 따라서 관련 행 집합의 마지막 작업에 대해 yield 지점을 설정해야 합니다.
예를 들어 원시 연락처 행과 연결된 데이터 행을 추가하는 세트의 마지막 작업 또는 단일 연락처와 관련된 행 집합의 마지막 작업에 대해 yield 포인트를 설정해야 합니다.
양보 지점은 원자성 작업의 단위이기도 합니다. 두 포인트 간의 모든 액세스는 단일 단위로 성공하거나 실패합니다. 산출 포인트를 설정하지 않으면 가장 작은 원자적 작업은 전체 일괄 작업입니다. 포인트를 사용하면 작업으로 인해 시스템 성능이 저하되는 것을 방지하면서 작업의 하위 집합이 원자적임을 보장할 수 있습니다.
수정 역참조
새 원시 연락처 행과 연결된 데이터 행을 ContentProviderOperation
객체 집합으로 삽입할 때는 원시 연락처의 _ID
값을 RAW_CONTACT_ID
값으로 삽입하여 데이터 행을 원시 연락처 행에 연결해야 합니다. 하지만 이 값은 데이터 행의 ContentProviderOperation
를 만들 때 사용할 수 없습니다. 아직 원시 연락처 행에 ContentProviderOperation
를 적용하지 않았기 때문입니다. 이 문제를 해결하기 위해 ContentProviderOperation.Builder
클래스에는 withValueBackReference()
메서드가 있습니다.
이 메서드를 사용하면 이전 작업의 결과로 열을 삽입하거나 수정할 수 있습니다.
withValueBackReference()
메서드에는 두 가지 인수가 있습니다.
-
key
- 키-값 쌍의 키입니다. 이 인수의 값은 수정하려는 테이블의 열 이름이어야 합니다.
-
previousResult
-
applyBatch()
의ContentProviderResult
객체 배열에 있는 값의 0부터 시작하는 색인입니다. 일괄 작업이 적용되면 각 작업의 결과가 결과의 중간 배열에 저장됩니다.previousResult
값은 이러한 결과 중 하나의 색인으로,key
값과 함께 검색되고 저장됩니다. 이렇게 하면 새 원시 연락처 레코드를 삽입하고_ID
값을 가져온 다음ContactsContract.Data
행을 추가할 때 값에 대한 '백 리퍼런스'를 만들 수 있습니다.전체 결과 배열은
applyBatch()
를 처음 호출할 때 생성되며, 크기는 제공한ContentProviderOperation
객체의ArrayList
크기와 같습니다. 그러나 결과 배열의 모든 요소는null
로 설정되며 아직 적용되지 않은 작업의 결과를 역참조하려고 하면withValueBackReference()
에서Exception
이 발생합니다.
다음 스니펫은 새로운 원시 연락처와 데이터를 일괄 삽입하는 방법을 보여줍니다. 여기에는 yield 지점을 설정하고 역참조를 사용하는 코드가 포함됩니다.
첫 번째 스니펫은 UI에서 연락처 데이터를 검색합니다. 이 시점에서 사용자는 새 원시 연락처를 추가할 계정을 이미 선택했습니다.
Kotlin
// Creates a contact entry from the current UI values, using the currently-selected account. private fun createContactEntry() { /* * Gets values from the UI */ val name = contactNameEditText.text.toString() val phone = contactPhoneEditText.text.toString() val email = contactEmailEditText.text.toString() val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition] val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]
자바
// Creates a contact entry from the current UI values, using the currently-selected account. protected void createContactEntry() { /* * Gets values from the UI */ String name = contactNameEditText.getText().toString(); String phone = contactPhoneEditText.getText().toString(); String email = contactEmailEditText.getText().toString(); int phoneType = contactPhoneTypes.get( contactPhoneTypeSpinner.getSelectedItemPosition()); int emailType = contactEmailTypes.get( contactEmailTypeSpinner.getSelectedItemPosition());
다음 스니펫은 원시 연락처 행을 ContactsContract.RawContacts
테이블에 삽입하는 작업을 만듭니다.
Kotlin
/* * Prepares the batch operation for inserting a new raw contact and its data. Even if * the Contacts Provider does not have any data for this person, you can't add a Contact, * only a raw contact. The Contacts Provider will then add a Contact automatically. */ // Creates a new array of ContentProviderOperation objects. val ops = arrayListOf<ContentProviderOperation>() /* * Creates a new raw contact with its account type (server type) and account name * (user's account). Remember that the display name is not stored in this row, but in a * StructuredName data row. No other data is required. */ var op: ContentProviderOperation.Builder = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type) // Builds the operation and adds it to the array of operations ops.add(op.build())
자바
/* * Prepares the batch operation for inserting a new raw contact and its data. Even if * the Contacts Provider does not have any data for this person, you can't add a Contact, * only a raw contact. The Contacts Provider will then add a Contact automatically. */ // Creates a new array of ContentProviderOperation objects. ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); /* * Creates a new raw contact with its account type (server type) and account name * (user's account). Remember that the display name is not stored in this row, but in a * StructuredName data row. No other data is required. */ ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType()) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName()); // Builds the operation and adds it to the array of operations ops.add(op.build());
그런 다음, 코드로 표시 이름, 전화 및 이메일 행에 대한 데이터 행을 생성합니다.
각 작업 빌더 객체는 withValueBackReference()
를 사용하여 RAW_CONTACT_ID
를 가져옵니다. 참조 포인트는 첫 번째 작업의 ContentProviderResult
객체로 돌아가서 원시 연락처 행을 추가하고 새 _ID
값을 반환합니다. 따라서 각 데이터 행은 RAW_CONTACT_ID
를 통해 속한 새 ContactsContract.RawContacts
행에 자동으로 연결됩니다.
이메일 행을 추가하는 ContentProviderOperation.Builder
객체는 withYieldAllowed()
로 플래그가 지정되어 있으며 이는 산출 지점을 설정합니다.
Kotlin
// Creates the display name for the new raw contact, as a StructuredName data row. op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * withValueBackReference sets the value of the first argument to the value of * the ContentProviderResult indexed by the second argument. In this particular * call, the raw contact ID column of the StructuredName data row is set to the * value of the result returned by the first operation, which is the one that * actually adds the raw contact row. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to StructuredName .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) // Sets the data row's display name to the name in the UI. .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) // Builds the operation and adds it to the array of operations ops.add(op.build()) // Inserts the specified phone number and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Phone .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Sets the phone number and type .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType) // Builds the operation and adds it to the array of operations ops.add(op.build()) // Inserts the specified email and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Email .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Sets the email address and type .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType) /* * Demonstrates a yield point. At the end of this insert, the batch operation's thread * will yield priority to other threads. Use after every set of operations that affect a * single contact, to avoid degrading performance. */ op.withYieldAllowed(true) // Builds the operation and adds it to the array of operations ops.add(op.build())
자바
// Creates the display name for the new raw contact, as a StructuredName data row. op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * withValueBackReference sets the value of the first argument to the value of * the ContentProviderResult indexed by the second argument. In this particular * call, the raw contact ID column of the StructuredName data row is set to the * value of the result returned by the first operation, which is the one that * actually adds the raw contact row. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to StructuredName .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) // Sets the data row's display name to the name in the UI. .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name); // Builds the operation and adds it to the array of operations ops.add(op.build()); // Inserts the specified phone number and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Phone .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Sets the phone number and type .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType); // Builds the operation and adds it to the array of operations ops.add(op.build()); // Inserts the specified email and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Email .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Sets the email address and type .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType); /* * Demonstrates a yield point. At the end of this insert, the batch operation's thread * will yield priority to other threads. Use after every set of operations that affect a * single contact, to avoid degrading performance. */ op.withYieldAllowed(true); // Builds the operation and adds it to the array of operations ops.add(op.build());
마지막 스니펫은 새 원시 연락처 및 데이터 행을 삽입하는 applyBatch()
호출을 보여줍니다.
Kotlin
// Ask the Contacts Provider to create a new contact Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})") Log.d(TAG, "Creating contact: $name") /* * Applies the array of ContentProviderOperation objects in batch. The results are * discarded. */ try { contentResolver.applyBatch(ContactsContract.AUTHORITY, ops) } catch (e: Exception) { // Display a warning val txt: String = getString(R.string.contactCreationFailure) Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show() // Log exception Log.e(TAG, "Exception encountered while inserting contact: $e") } }
자바
// Ask the Contacts Provider to create a new contact Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" + selectedAccount.getType() + ")"); Log.d(TAG,"Creating contact: " + name); /* * Applies the array of ContentProviderOperation objects in batch. The results are * discarded. */ try { getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { // Display a warning Context ctx = getApplicationContext(); CharSequence txt = getString(R.string.contactCreationFailure); int duration = Toast.LENGTH_SHORT; Toast toast = Toast.makeText(ctx, txt, duration); toast.show(); // Log exception Log.e(TAG, "Exception encountered while inserting contact: " + e); } }
일괄 작업을 사용하면 기본 저장소를 잠그지 않고도 수정 트랜잭션을 적용하는 방법인 낙관 동시 실행 제어를 구현할 수도 있습니다. 이 메서드를 사용하려면 트랜잭션을 적용한 다음 동시에 적용되었을 수 있는 다른 수정사항을 확인합니다. 일치하지 않는 수정이 발생한 경우 트랜잭션을 롤백하고 다시 시도합니다.
낙관적 동시 실행 제어는 한 번에 한 명의 사용자만 있고 데이터 저장소에 대한 동시 액세스가 드문 휴대기기에 유용합니다. 잠금이 사용되지 않으므로 잠금을 설정하거나 다른 트랜잭션이 잠금을 해제할 때까지 기다리는 데 시간이 낭비되지 않습니다.
단일 ContactsContract.RawContacts
행을 업데이트하는 동안 낙관적 동시 실행 제어를 사용하려면 다음 단계를 따르세요.
-
가져오는 다른 데이터와 함께 원시 연락처의
VERSION
열을 가져옵니다. -
newAssertQuery(Uri)
메서드를 사용하여 제약 조건을 적용하는 데 적합한ContentProviderOperation.Builder
객체를 만듭니다. 콘텐츠 URI의 경우 원시 연락처의_ID
가 추가된RawContacts.CONTENT_URI
를 사용합니다. -
ContentProviderOperation.Builder
객체의 경우withValue()
를 호출하여VERSION
열을 방금 가져온 버전 번호와 비교합니다. -
동일한
ContentProviderOperation.Builder
의 경우withExpectedCount()
를 호출하여 이 어설션으로 하나의 행만 테스트되도록 합니다. -
build()
를 호출하여ContentProviderOperation
객체를 만든 다음 이 객체를applyBatch()
에 전달하는ArrayList
의 첫 번째 객체로 추가합니다. - 일괄 트랜잭션을 적용합니다.
행을 읽은 시점과 수정하려는 시점 사이에 다른 작업에 의해 원시 연락처 행이 업데이트되면 '어설션' ContentProviderOperation
이 실패하고 전체 일괄 작업이 취소됩니다. 그런 다음 일괄 처리를 다시 시도하거나 다른 조치를 취할 수 있습니다.
다음 스니펫은 CursorLoader
을 사용하여 단일 원시 연락처를 쿼리한 후 '어설션' ContentProviderOperation
를 만드는 방법을 보여줍니다.
Kotlin
/* * The application uses CursorLoader to query the raw contacts table. The system calls this method * when the load is finished. */ override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) { // Gets the raw contact's _ID and VERSION values rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)) mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION)) } ... // Sets up a Uri for the assert operation val rawContactUri: Uri = ContentUris.withAppendedId( ContactsContract.RawContacts.CONTENT_URI, rawContactID ) // Creates a builder for the assert operation val assertOp: ContentProviderOperation.Builder = ContentProviderOperation.newAssertQuery(rawContactUri).apply { // Adds the assertions to the assert operation: checks the version withValue(SyncColumns.VERSION, mVersion) // and count of rows tested withExpectedCount(1) } // Creates an ArrayList to hold the ContentProviderOperation objects val ops = arrayListOf<ContentProviderOperation>() ops.add(assertOp.build()) // You would add the rest of your batch operations to "ops" here ... // Applies the batch. If the assert fails, an Exception is thrown try { val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops) } catch (e: OperationApplicationException) { // Actions you want to take if the assert operation fails go here }
자바
/* * The application uses CursorLoader to query the raw contacts table. The system calls this method * when the load is finished. */ public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { // Gets the raw contact's _ID and VERSION values rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)); mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION)); } ... // Sets up a Uri for the assert operation Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactID); // Creates a builder for the assert operation ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(rawContactUri); // Adds the assertions to the assert operation: checks the version and count of rows tested assertOp.withValue(SyncColumns.VERSION, mVersion); assertOp.withExpectedCount(1); // Creates an ArrayList to hold the ContentProviderOperation objects ArrayList ops = new ArrayList<ContentProviderOperation>; ops.add(assertOp.build()); // You would add the rest of your batch operations to "ops" here ... // Applies the batch. If the assert fails, an Exception is thrown try { ContentProviderResult[] results = getContentResolver().applyBatch(AUTHORITY, ops); } catch (OperationApplicationException e) { // Actions you want to take if the assert operation fails go here }
인텐트로 검색 및 수정
기기의 연락처 애플리케이션에 인텐트를 전송하면 연락처 제공자에 간접적으로 액세스할 수 있습니다. 이 인텐트는 사용자가 연락처 관련 작업을 할 수 있는 기기의 연락처 애플리케이션 UI를 시작합니다. 사용자가 이런 액세스 유형으로 할 수 있는 일은 다음과 같습니다.
- 목록에서 연락처를 선택하고 추가 작업을 위해 앱에 반환시킵니다.
- 기존 연락처의 데이터를 수정합니다.
- 새 원시 데이터를 해당되는 계정 중 아무 곳에나 삽입합니다.
- 연락처 또는 연락처 데이터를 삭제합니다.
사용자가 데이터를 삽입하거나 업데이트하는 경우 먼저 데이터를 수집하고 인텐트의 일부로 전송할 수 있습니다.
인텐트를 사용하여 기기의 연락처 애플리케이션을 통해 연락처 제공업체에 액세스하는 경우 제공업체에 액세스하기 위한 자체 UI나 코드를 작성할 필요가 없습니다. 또한 제공자를 읽거나 쓰기 위해 권한을 요청할 필요도 없습니다. 기기의 연락처 애플리케이션은 연락처의 읽기 권한을 개발자에게 위임할 수 있으며, 다른 애플리케이션을 통해 제공자를 수정하고 있으므로 쓰기 권한이 필요하지 않습니다.
제공자에 액세스하기 위해 인텐트를 전송하는 일반적인 프로세스는
콘텐츠 제공자 기본사항 가이드의 '인텐트를 통한 데이터 액세스' 섹션에 자세히 설명되어 있습니다. 사용 가능한 작업에 사용하는 작업, MIME 유형, 데이터 값은 표 4에 요약되어 있으며 putExtra()
와 함께 사용할 수 있는 추가 값은 ContactsContract.Intents.Insert
의 참조 문서에 나와 있습니다.
표 4. 연락처 제공자 인텐트
작업 | 작업 | 데이터 | MIME 유형 | 참고 |
---|---|---|---|---|
목록에서 연락처 선택하기 | ACTION_PICK |
다음 중 하나입니다.
|
사용되지 않음 |
제공하는 콘텐츠 URI 유형에 따라 원시 연락처 목록 또는 원시 연락처의 데이터 목록을 표시합니다.
선택한 행의 콘텐츠 URI를 반환하는 |
새 원시 연락처 삽입하기 | Insert.ACTION |
해당 사항 없음 |
RawContacts.CONTENT_TYPE : 원시 연락처 집합의 MIME 유형입니다.
|
기기의 연락처 애플리케이션의 연락처 추가 화면을 표시합니다. 인텐트에 추가한 추가 값이 표시됩니다. startActivityForResult() 와 함께 전송되면 새로 추가된 원시 연락처의 콘텐츠 URI가 Intent 인수의 'data' 필드에서 활동의 onActivityResult() 콜백 메서드로 다시 전달됩니다. 값을 가져오려면 getData() 를 호출합니다.
|
연락처 수정하기 | ACTION_EDIT |
CONTENT_LOOKUP_URI 를 사용합니다. 편집기 활동을 통해 사용자는 이 연락처와 연결된 모든 데이터를 수정할 수 있습니다.
|
Contacts.CONTENT_ITEM_TYPE , 단일 연락처 |
연락처 애플리케이션에 연락처 편집 화면을 표시합니다. 인텐트에 추가한 추가 값이 표시됩니다. 사용자가 완료를 클릭하여 수정사항을 저장하면 활동이 포그라운드로 돌아갑니다. |
데이터를 추가할 수도 있는 선택 도구를 표시합니다. | ACTION_INSERT_OR_EDIT |
해당 사항 없음 |
CONTENT_ITEM_TYPE
|
이 인텐트는 항상 연락처 앱의 선택 도구 화면을 표시합니다. 사용자는 수정할 연락처를 선택하거나 새 연락처를 추가할 수 있습니다. 사용자의 선택에 따라 수정 또는 추가 화면이 표시되고 인텐트에 전달한 추가 데이터가 표시됩니다. 앱에서 이메일이나 전화번호와 같은 연락처 데이터를 표시하는 경우 이 인텐트를 사용하여 사용자가 기존 연락처에 데이터를 추가할 수 있도록 허용합니다.
연락처
참고: 사용자가 항상 기존 이름을 선택하거나 새 이름을 추가하므로 이 인텐트의 추가 항목에 이름 값을 전송할 필요가 없습니다. 또한 이름을 전송하고 사용자가 수정을 선택하면 연락처 앱에서 전송한 이름을 표시하여 이전 값을 덮어씁니다. 사용자가 이를 알아채지 못하고 수정사항을 저장하면 이전 값이 손실됩니다. |
기기의 연락처 앱에서는 인텐트를 사용하여 원시 연락처 또는 해당 데이터를 삭제할 수 없습니다. 대신 원시 연락처를 삭제하려면 ContentResolver.delete()
또는 ContentProviderOperation.newDelete()
를 사용하세요.
다음 스니펫은 새 원시 연락처 및 데이터를 삽입하는 인텐트를 구성하고 전송하는 방법을 보여줍니다.
Kotlin
// Gets values from the UI val name = contactNameEditText.text.toString() val phone = contactPhoneEditText.text.toString() val email = contactEmailEditText.text.toString() val company = companyName.text.toString() val jobtitle = jobTitle.text.toString() /* * Demonstrates adding data rows as an array list associated with the DATA key */ // Defines an array list to contain the ContentValues objects for each row val contactData = arrayListOf<ContentValues>() /* * Defines the raw contact row */ // Sets up the row as a ContentValues object val rawContactRow = ContentValues().apply { // Adds the account type and name to the row put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type) put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name) } // Adds the row to the array contactData.add(rawContactRow) /* * Sets up the phone number data row */ // Sets up the row as a ContentValues object val phoneRow = ContentValues().apply { // Specifies the MIME type for this data row (all data rows must be marked by their type) put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Adds the phone number and its type to the row put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) } // Adds the row to the array contactData.add(phoneRow) /* * Sets up the email data row */ // Sets up the row as a ContentValues object val emailRow = ContentValues().apply { // Specifies the MIME type for this data row (all data rows must be marked by their type) put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Adds the email address and its type to the row put(ContactsContract.CommonDataKinds.Email.ADDRESS, email) } // Adds the row to the array contactData.add(emailRow) // Creates a new intent for sending to the device's contacts application val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply { // Sets the MIME type to the one expected by the insertion activity type = ContactsContract.RawContacts.CONTENT_TYPE // Sets the new contact name putExtra(ContactsContract.Intents.Insert.NAME, name) // Sets the new company and job title putExtra(ContactsContract.Intents.Insert.COMPANY, company) putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle) /* * Adds the array to the intent's extras. It must be a parcelable object in order to * travel between processes. The device's contacts app expects its key to be * Intents.Insert.DATA */ putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData) } // Send out the intent to start the device's contacts app in its add contact activity. startActivity(insertIntent)
자바
// Gets values from the UI String name = contactNameEditText.getText().toString(); String phone = contactPhoneEditText.getText().toString(); String email = contactEmailEditText.getText().toString(); String company = companyName.getText().toString(); String jobtitle = jobTitle.getText().toString(); // Creates a new intent for sending to the device's contacts application Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION); // Sets the MIME type to the one expected by the insertion activity insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE); // Sets the new contact name insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name); // Sets the new company and job title insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company); insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle); /* * Demonstrates adding data rows as an array list associated with the DATA key */ // Defines an array list to contain the ContentValues objects for each row ArrayList<ContentValues> contactData = new ArrayList<ContentValues>(); /* * Defines the raw contact row */ // Sets up the row as a ContentValues object ContentValues rawContactRow = new ContentValues(); // Adds the account type and name to the row rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType()); rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName()); // Adds the row to the array contactData.add(rawContactRow); /* * Sets up the phone number data row */ // Sets up the row as a ContentValues object ContentValues phoneRow = new ContentValues(); // Specifies the MIME type for this data row (all data rows must be marked by their type) phoneRow.put( ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE ); // Adds the phone number and its type to the row phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone); // Adds the row to the array contactData.add(phoneRow); /* * Sets up the email data row */ // Sets up the row as a ContentValues object ContentValues emailRow = new ContentValues(); // Specifies the MIME type for this data row (all data rows must be marked by their type) emailRow.put( ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ); // Adds the email address and its type to the row emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email); // Adds the row to the array contactData.add(emailRow); /* * Adds the array to the intent's extras. It must be a parcelable object in order to * travel between processes. The device's contacts app expects its key to be * Intents.Insert.DATA */ insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); // Send out the intent to start the device's contacts app in its add contact activity. startActivity(insertIntent);
데이터 무결성
연락처 저장소에는 사용자가 정확하고 최신 상태일 것으로 기대하는 중요하고 민감한 데이터가 포함되어 있으므로 연락처 제공업체에는 데이터 무결성에 관한 잘 정의된 규칙이 있습니다. 연락처 데이터를 수정할 때 이러한 규칙을 준수하는 것은 개발자의 책임입니다. 중요한 규칙은 다음과 같습니다.
-
추가하는
ContactsContract.RawContacts
행마다 항상ContactsContract.CommonDataKinds.StructuredName
행을 추가합니다. -
ContactsContract.Data
테이블에ContactsContract.CommonDataKinds.StructuredName
행이 없는ContactsContract.RawContacts
행은 집계 중에 문제가 발생할 수 있습니다. -
항상 새
ContactsContract.Data
행을 상위ContactsContract.RawContacts
행에 연결합니다. -
ContactsContract.RawContacts
에 연결되지 않은ContactsContract.Data
행은 기기의 연락처 애플리케이션에 표시되지 않으며 동기화 어댑터에 문제가 발생할 수 있습니다. - 개발자 본인의 소유인 원시 연락처에 대한 데이터만 변경하세요.
- 연락처 제공업체는 일반적으로 여러 계정 유형/온라인 서비스의 데이터를 관리합니다. 애플리케이션이 나에게 속한 행의 데이터만 수정하거나 삭제하고 내가 관리하는 계정 유형과 이름으로 데이터만 삽입해야 합니다.
-
항상
ContactsContract
및 그 서브클래스에 정의된 상수를 사용해 사용자 인증 정보, 콘텐츠 URI, URI 경로, 열 이름, MIME 유형,TYPE
값을 지정하세요. - 이러한 상수를 사용하면 오류를 방지할 수 있습니다. 상수가 지원 중단된 경우 컴파일러 경고도 표시됩니다.
맞춤 데이터 행
자체 맞춤 MIME 유형을 만들고 사용하면 ContactsContract.Data
테이블에 자체 데이터 행을 삽입, 수정, 삭제, 검색할 수 있습니다. 행은 ContactsContract.DataColumns
에 정의된 열을 사용하도록 제한되지만 자체 유형별 열 이름을 기본 열 이름에 매핑할 수 있습니다. 기기의 연락처 애플리케이션에서 행의 데이터가 표시되지만 수정하거나 삭제할 수 없으며 사용자는 추가 데이터를 추가할 수 없습니다. 사용자가 맞춤 데이터 행을 수정할 수 있도록 하려면 자체 애플리케이션에 편집기 활동을 제공해야 합니다.
맞춤 데이터를 표시하려면 <ContactsAccountType>
요소와 하나 이상의 <ContactsDataKind>
하위 요소가 포함된 contacts.xml
파일을 제공합니다. 자세한 내용은 <ContactsDataKind> element
섹션을 참고하세요.
맞춤 MIME 유형에 관한 자세한 내용은 콘텐츠 제공자 만들기 가이드를 참고하세요.
연락처 제공자 동기화 어댑터
연락처 제공자는 특히 기기와 온라인 서비스 간의 연락처 데이터 동기화를 처리하도록 설계되었습니다. 이를 통해 사용자는 기존 데이터를 새 기기에 다운로드하고 기존 데이터를 새 계정에 업로드할 수 있습니다. 또한 동기화를 통해 추가 및 변경의 소스와 관계없이 사용자가 최신 데이터를 사용할 수 있습니다. 동기화의 또 다른 이점은 기기가 네트워크에 연결되어 있지 않아도 연락처 데이터를 사용할 수 있다는 점입니다.
동기화는 다양한 방법으로 구현할 수 있지만 Android 시스템은 다음 작업을 자동화하는 플러그인 동기화 프레임워크를 제공합니다.
- 네트워크 가용성을 확인합니다.
- 사용자 기본 설정에 따라 동기화를 예약하고 실행합니다.
- 중단된 동기화를 다시 시작합니다.
이 프레임워크를 사용하려면 개발자가 동기화 어댑터 플러그인을 직접 제공해야 합니다. 각 동기화 어댑터는 서비스 및 콘텐츠 제공업체마다 고유하지만 동일한 서비스의 여러 계정 이름을 처리할 수 있습니다. 또한 프레임워크는 동일한 서비스 및 제공자에 대해 여러 동기화 어댑터를 허용합니다.
동기화 어댑터 클래스 및 파일
동기화 어댑터를 AbstractThreadedSyncAdapter
의 서브클래스로 구현하고 Android 애플리케이션의 일부로 설치합니다. 시스템은 애플리케이션 매니페스트의 요소와 매니페스트에서 가리키는 특수 XML 파일에서 동기화 어댑터에 관해 학습합니다. XML 파일은 온라인 서비스의 계정 유형과 콘텐츠 제공업체의 권한을 정의하며, 이 두 가지가 어댑터를 고유하게 식별합니다. 동기화 어댑터는 사용자가 동기화 어댑터의 계정 유형에 대한 계정을 추가하고 동기화 어댑터가 동기화하는 콘텐츠 제공업체의 동기화를 사용 설정할 때까지 활성화되지 않습니다. 이 시점에서 시스템은 어댑터 관리를 시작하여 콘텐츠 제공자와 서버 간에 동기화하는 데 필요한 경우 어댑터를 호출합니다.
참고: 동기화 어댑터 식별의 일부로 계정 유형을 사용하면 시스템에서 동일한 조직의 서로 다른 서비스에 액세스하는 동기화 어댑터를 감지하고 그룹화할 수 있습니다. 예를 들어 Google 온라인 서비스의 동기화 어댑터는 모두 동일한 계정 유형 com.google
을 사용합니다. 사용자가 기기에 Google 계정을 추가하면 Google 서비스용으로 설치된 모든 동기화 어댑터가 함께 표시됩니다. 표시된 각 동기화 어댑터는 기기의 다른 콘텐츠 제공업체와 동기화됩니다.
대부분의 서비스에서는 사용자가 데이터에 액세스하기 전에 신원을 확인해야 하므로 Android 시스템은 동기화 어댑터 프레임워크와 유사하며 동기화 어댑터 프레임워크와 함께 사용되는 인증 프레임워크를 제공합니다. 인증 프레임워크는 AbstractAccountAuthenticator
의 서브클래스인 플러그인 인증자를 사용합니다. 인증자는 다음 단계에 따라 사용자의 신원을 확인합니다.
- 사용자의 이름, 비밀번호 또는 유사한 정보 (사용자의 사용자 인증 정보)를 수집합니다.
- 사용자 인증 정보를 서비스로 전송합니다.
- 서비스의 답장을 검사합니다.
서비스가 사용자 인증 정보를 수락하면 인증자는 나중에 사용할 수 있도록 사용자 인증 정보를 저장할 수 있습니다. 플러그인 인증자 프레임워크로 인해 AccountManager
는 인증자가 지원하고 노출하기로 선택한 모든 authtokens(예: OAuth2 authtokens)에 대한 액세스를 제공할 수 있습니다.
인증이 필수는 아니지만, 대부분 연락처 서비스는 이를 사용합니다. 하지만 인증을 수행하기 위해 Android 인증 프레임워크를 사용할 필요는 없습니다.
동기화 어댑터 구현
연락처 제공자의 동기화 어댑터를 구현하려면 먼저 다음이 포함된 Android 애플리케이션을 만듭니다.
-
동기화 어댑터에 바인딩하라는 시스템의 요청에 응답하는
Service
구성요소입니다. -
시스템이 동기화를 실행하려고 하면 서비스의
onBind()
메서드를 호출하여 동기화 어댑터의IBinder
를 가져옵니다. 이렇게 하면 시스템이 어댑터의 메서드에 대한 교차 프로세스 호출을 실행할 수 있습니다. -
AbstractThreadedSyncAdapter
의 구체적인 서브클래스로 구현된 실제 동기화 어댑터입니다. - 이 클래스는 서버에서 데이터를 다운로드하고, 기기에서 데이터를 업로드하고, 충돌을 해결하는 작업을 실행합니다. 어댑터의 기본 작업은
onPerformSync()
메서드에서 실행됩니다. 이 클래스는 반드시 단일 항목으로 인스턴스화해야 합니다. -
Application
의 서브클래스입니다. -
이 클래스는 동기화 어댑터 싱글톤의 팩토리 역할을 합니다.
onCreate()
메서드를 사용하여 동기화 어댑터를 인스턴스화하고 정적 'getter' 메서드를 제공하여 동기화 어댑터 서비스의onBind()
메서드에 싱글톤을 반환합니다. -
선택사항: 시스템의 사용자 인증 요청에 응답하는
Service
구성요소입니다. -
AccountManager
이 이 서비스를 시작하여 인증 프로세스를 시작합니다. 서비스의onCreate()
메서드는 인증자 객체를 인스턴스화합니다. 시스템이 애플리케이션의 동기화 어댑터에 대한 사용자 계정을 인증하려고 하면 서비스의onBind()
메서드를 호출하여 인증자의IBinder
를 가져옵니다. 이렇게 하면 시스템이 인증자의 메서드에 대한 교차 프로세스 호출을 실행할 수 있습니다. -
선택사항: 인증 요청을 처리하는
AbstractAccountAuthenticator
의 구체적인 서브클래스입니다. - 이 클래스는
AccountManager
가 서버에서 사용자의 사용자 인증 정보를 인증하기 위해 호출하는 메서드를 제공합니다. 인증 절차의 세부정보는 사용 중인 서버 기술에 따라 크게 다릅니다. 인증에 관한 자세한 내용은 서버 소프트웨어의 문서를 참고하세요. - 동기화 어댑터와 서버의 인증자를 정의하는 XML 파일.
-
앞서 설명한 동기화 어댑터 및 인증자 서비스 구성요소는 애플리케이션 매니페스트의
<service>
요소에 정의됩니다. 이러한 요소에는 시스템에 특정 데이터를 제공하는<meta-data>
하위 요소가 포함됩니다.
소셜 스트림 데이터
android.provider.ContactsContract.StreamItems 및 android.provider.ContactsContract.StreamItemPhotos 테이블은 소셜 네트워크에서 수신되는 데이터를 관리합니다. 자체 네트워크의 스트림 데이터를 이러한 테이블에 추가하는 동기화 어댑터를 작성하거나 이러한 테이블에서 스트림 데이터를 읽고 자체 애플리케이션에 표시하거나 둘 다를 실행할 수 있습니다. 이러한 기능을 사용하면 소셜 네트워킹 서비스와 애플리케이션을 Android의 소셜 네트워킹 환경에 통합할 수 있습니다.
소셜 스트림 텍스트
스트림 항목은 항상 원시 연락처와 연관됩니다. android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID는 원시 연락처의 _ID
값에 연결됩니다. 원시 연락처의 계정 유형 및 계정 이름도 스트림 항목 행에 저장됩니다.
스트림에서 가져온 데이터는 다음 열에 저장합니다.
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
- 필수사항. 이 스트림 항목과 연결된 원시 연락처의 사용자 계정 유형입니다. 스트림 항목을 삽입할 때 이 값을 설정하는 것을 잊지 마세요.
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
- 필수사항. 이 스트림 항목과 연결된 원시 연락처의 사용자 계정 이름입니다. 스트림 항목을 삽입할 때 이 값을 설정하는 것을 잊지 마세요.
- 식별자 열
-
필수사항. 스트림 항목을 삽입할 때 다음 식별자 열을 삽입해야 합니다.
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: 이 스트림 항목과 연결된 연락처의 android.provider.BaseColumns#_ID 값입니다.
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: 이 스트림 항목이 연결된 연락처의 android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY 값입니다.
- android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: 이 스트림 항목과 연결된 원시 연락처의 android.provider.BaseColumns#_ID 값입니다.
- android.provider.ContactsContract.StreamItemsColumns#COMMENTS
- 선택사항입니다. 스트림 항목의 시작 부분에 표시할 수 있는 요약 정보를 저장합니다.
- android.provider.ContactsContract.StreamItemsColumns#TEXT
-
스트림 항목의 텍스트로, 항목 소스에서 게시한 콘텐츠 또는 스트림 항목을 생성한 작업에 관한 설명입니다. 이 열에는
fromHtml()
로 렌더링할 수 있는 모든 형식 및 삽입된 리소스 이미지가 포함될 수 있습니다. 제공업체는 긴 콘텐츠를 자르거나 생략할 수 있지만 태그가 중단되지 않도록 합니다. - android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
- 스트림 항목이 삽입되거나 업데이트된 시간을 에포크 이후 밀리초 형식으로 포함하는 텍스트 문자열입니다. 스트림 항목을 삽입하거나 업데이트하는 애플리케이션이 이 열을 유지관리합니다. 이 열은 연락처 제공업체에서 자동으로 유지관리하지 않습니다.
스트림 항목의 식별 정보를 표시하려면 android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL, android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE를 사용하여 애플리케이션의 리소스에 연결합니다.
android.provider.ContactsContract.StreamItems 테이블에는 동기화 어댑터 전용으로 사용하는 android.provider.ContactsContract.StreamItemsColumns#SYNC1~android.provider.ContactsContract.StreamItemsColumns#SYNC4 열도 포함되어 있습니다.
소셜 스트림 사진
android.provider.ContactsContract.StreamItemPhotos 테이블은 스트림 항목과 연결된 사진을 저장합니다. 테이블의 android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID 열은 android.provider.ContactsContract.StreamItems 테이블의 _ID
열에 있는 값에 연결됩니다. 사진 참조는 다음 열의 테이블에 저장됩니다.
- android.provider.ContactsContract.StreamItemPhotos#PHOTO 열(BLOB)
- 저장 및 표시를 위해 제공업체에서 크기를 조절한 사진의 바이너리 표현입니다. 이 열은 사진을 저장하는 데 사용했던 이전 버전의 연락처 제공업체와의 하위 호환성을 위해 사용할 수 있습니다. 그러나 현재 버전에서는 이 열을 사용하여 사진을 저장하면 안 됩니다. 대신 android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID 또는 android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (둘 다 다음에 설명됨)를 사용하여 파일에 사진을 저장합니다. 이제 이 열에 읽을 수 있는 사진 썸네일이 포함됩니다.
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
원시 연락처의 사진에 대한 숫자 식별자입니다. 이 값을 상수
DisplayPhoto.CONTENT_URI
에 추가하여 단일 사진 파일을 가리키는 콘텐츠 URI를 가져온 다음openAssetFileDescriptor()
를 호출하여 사진 파일의 핸들을 가져옵니다. - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
- 이 행으로 표시된 사진의 사진 파일을 직접 가리키는 콘텐츠 URI입니다.
이 URI를 사용하여
openAssetFileDescriptor()
를 호출하여 사진 파일의 핸들을 가져옵니다.
소셜 스트림 테이블 사용
이들 테이블은 연락처 제공자의 다른 주요 테이블과 똑같이 작동하지만, 다음 예외가 적용됩니다.
- 이 테이블에는 추가 액세스 권한이 필요합니다. 이를 읽으려면 애플리케이션에 android.Manifest.permission#READ_SOCIAL_STREAM 권한이 있어야 합니다. 이를 수정하려면 애플리케이션에 android.Manifest.permission#WRITE_SOCIAL_STREAM 권한이 있어야 합니다.
-
android.provider.ContactsContract.StreamItems 테이블의 경우 각 원시 연락처에 저장되는 행 수가 제한됩니다. 이 한도에 도달하면 연락처 제공자가 가장 오래된 android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP가 있는 행을 자동으로 삭제하여 새 스트림 항목 행을 위한 공간을 만듭니다. 한도를 가져오려면 콘텐츠 URI android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI에 쿼리를 실행합니다. 콘텐츠 URI를 제외한 모든 인수는
null
로 설정할 수 있습니다. 이 쿼리는 단일 열 android.provider.ContactsContract.StreamItems#MAX_ITEMS가 있는 단일 행을 포함하는 커서를 반환합니다.
android.provider.ContactsContract.StreamItems.StreamItemPhotos 클래스는 단일 스트림 항목의 사진 행이 포함된 android.provider.ContactsContract.StreamItemPhotos의 하위 테이블을 정의합니다.
소셜 스트림 상호작용
연락처 제공업체에서 관리하는 소셜 스트림 데이터는 기기의 연락처 애플리케이션과 함께 소셜 네트워킹 시스템을 기존 연락처에 연결하는 강력한 방법을 제공합니다. 사용할 수 있는 기능은 다음과 같습니다.
- 동기화 어댑터를 사용하여 소셜 네트워킹 서비스를 연락처 제공업체에 동기화하면 사용자의 연락처에 대한 최근 활동을 검색하여 나중에 사용할 수 있도록 android.provider.ContactsContract.StreamItems 및 android.provider.ContactsContract.StreamItemPhotos 테이블에 저장할 수 있습니다.
- 정기 동기화 외에도 동기화 어댑터를 트리거하여 사용자가 볼 연락처를 선택할 때 추가 데이터를 가져올 수 있습니다. 이렇게 하면 동기화 어댑터가 연락처의 고해상도 사진과 최신 스트림 항목을 가져올 수 있습니다.
- 기기의 연락처 애플리케이션 및 연락처 제공자에 알림을 등록하면 연락처가 조회될 때 인텐트를 수신하고 이 시점에서 서비스에서 연락처의 상태를 업데이트할 수 있습니다. 이 접근 방식은 동기화 어댑터로 전체 동기화를 실행하는 것보다 빠르고 대역폭을 덜 사용할 수 있습니다.
- 사용자가 기기의 연락처 애플리케이션에서 연락처를 확인하는 동안 소셜 네트워킹 서비스에 연락처를 추가할 수 있습니다. '연락처 초대' 기능을 사용 설정하면 됩니다. 이 기능은 네트워크에 기존 연락처를 추가하는 활동과 기기의 연락처 애플리케이션 및 연락처 제공업체에 애플리케이션 세부정보를 제공하는 XML 파일을 조합하여 사용 설정합니다.
스트림 항목을 연락처 제공자와 정기적으로 동기화하는 것은 다른 동기화와 동일합니다. 동기화에 관해 자세히 알아보려면 연락처 제공자 동기화 어댑터 섹션을 참고하세요. 알림을 등록하고 연락처를 초대하는 방법은 다음 두 섹션에서 설명합니다.
소셜 네트워킹 뷰를 처리하기 위한 등록
사용자가 동기화 어댑터에서 관리하는 연락처를 볼 때 알림을 수신하도록 동기화 어댑터를 등록하려면 다음 단계를 따르세요.
-
프로젝트의
res/xml/
디렉터리에contacts.xml
라는 파일을 만듭니다. 이미 이 파일이 있다면 이 절차를 건너뛰어도 됩니다. -
이 파일에
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
요소를 추가합니다. 이 요소가 이미 존재한다면 이 절차를 건너뛰어도 됩니다. -
사용자가 기기의 연락처 애플리케이션에서 연락처 세부정보 페이지를 열 때 알림을 받는 서비스를 등록하려면 요소에
viewContactNotifyService="serviceclass"
속성을 추가합니다. 여기서serviceclass
는 기기의 연락처 애플리케이션에서 인텐트를 수신해야 하는 서비스의 정규화된 클래스 이름입니다. 알림 서비스의 경우IntentService
를 확장하는 클래스를 사용하여 서비스가 인텐트를 수신하도록 허용합니다. 수신 인텐트의 데이터에는 사용자가 클릭한 원시 연락처의 콘텐츠 URI가 포함됩니다. 알림 서비스에서 동기화 어댑터를 바인딩한 다음 호출하여 원시 연락처의 데이터를 업데이트할 수 있습니다.
사용자가 스트림 항목이나 사진, 또는 그 두 가지를 모두 클릭할 때 호출할 Activity를 등록하는 방법:
-
프로젝트의
res/xml/
디렉터리에contacts.xml
라는 파일을 만듭니다. 이미 이 파일이 있다면 이 절차를 건너뛰어도 됩니다. -
이 파일에
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
요소를 추가합니다. 이 요소가 이미 존재한다면 이 절차를 건너뛰어도 됩니다. -
사용자가 기기의 연락처 애플리케이션에서 스트림 항목을 클릭할 때 이를 처리하도록 활동 중 하나를 등록하려면 요소에
viewStreamItemActivity="activityclass"
속성을 추가합니다. 여기서activityclass
는 기기의 연락처 애플리케이션에서 인텐트를 수신해야 하는 활동의 정규화된 클래스 이름입니다. -
사용자가 기기의 연락처 애플리케이션에서 스트림 사진을 클릭할 때 이를 처리하도록 활동 중 하나를 등록하려면 요소에
viewStreamItemPhotoActivity="activityclass"
속성을 추가합니다. 여기서activityclass
는 기기의 연락처 애플리케이션에서 인텐트를 수신해야 하는 활동의 정규화된 클래스 이름입니다.
<ContactsAccountType>
요소는 <ContactsAccountType> 요소 섹션에 자세히 설명되어 있습니다.
수신되는 인텐트에는 사용자가 클릭한 항목 또는 사진의 콘텐츠 URI가 들어 있습니다. 텍스트 항목과 사진에 각기 별도의 Activity를 적용하려면, 두 특성을 모두 같은 파일에서 사용하세요.
소셜 네트워킹 서비스로 상호작용
사용자가 소셜 네트워킹 사이트에 연락처를 초대하기 위해 기기의 연락처 애플리케이션을 나가지 않아도 됩니다. 대신 기기의 연락처 앱이 연락처를 활동 중 하나에 초대하는 인텐트를 전송하도록 할 수 있습니다. 설정 방법은 다음과 같습니다.
-
프로젝트의
res/xml/
디렉터리에contacts.xml
라는 파일을 만듭니다. 이미 이 파일이 있다면 이 절차를 건너뛰어도 됩니다. -
이 파일에
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
요소를 추가합니다. 이 요소가 이미 존재한다면 이 절차를 건너뛰어도 됩니다. -
다음 속성을 추가합니다.
inviteContactActivity="activityclass"
-
inviteContactActionLabel="@string/invite_action_label"
activityclass
값은 인텐트를 수신해야 하는 활동의 정규화된 클래스 이름입니다.invite_action_label
값은 기기의 연락처 애플리케이션에 있는 연결 추가 메뉴에 표시되는 텍스트 문자열입니다.
참고: ContactsSource
는 ContactsAccountType
의 지원 중단된 태그 이름입니다.
contacts.xml 참조
contacts.xml
파일에는 동기화 어댑터와 애플리케이션이 연락처 애플리케이션 및 연락처 제공업체와 상호작용하는 방식을 제어하는 XML 요소가 포함되어 있습니다. 이러한 요소는 다음 섹션에서 설명합니다.
<ContactsAccountType> 요소
<ContactsAccountType>
요소는 애플리케이션과 연락처 애플리케이션 간의 상호작용을 제어합니다. 이 요소에는 다음 구문이 있습니다.
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android" inviteContactActivity="activity_name" inviteContactActionLabel="invite_command_text" viewContactNotifyService="view_notify_service" viewGroupActivity="group_view_activity" viewGroupActionLabel="group_action_text" viewStreamItemActivity="viewstream_activity_name" viewStreamItemPhotoActivity="viewphotostream_activity_name">
포함된 위치:
res/xml/contacts.xml
다음을 포함할 수 있습니다.
<ContactsDataKind>
설명:
사용자가 소셜 네트워크에 연락처를 초대하거나 소셜 네트워킹 스트림 중 하나가 업데이트될 때 사용자에게 알림을 보내는 등의 작업을 할 수 있는 Android 구성요소 및 UI 라벨을 선언합니다.
<ContactsAccountType>
의 속성에는 속성 접두사 android:
가 필요하지 않습니다.
속성:
inviteContactActivity
- 사용자가 기기의 연락처 애플리케이션에서 연결 추가를 선택할 때 활성화할 애플리케이션의 활동의 정규화된 클래스 이름입니다.
inviteContactActionLabel
-
inviteContactActivity
에 지정된 활동에 대해 연결 추가 메뉴에 표시되는 텍스트 문자열입니다. 예를 들어 '내 네트워크에서 팔로우' 문자열을 사용할 수 있습니다. 이 라벨에는 문자열 리소스 식별자를 사용할 수 있습니다. viewContactNotifyService
- 사용자가 연락처를 볼 때 알림을 수신해야 하는 애플리케이션의 서비스의 정규화된 클래스 이름입니다. 이 알림은 기기의 연락처 애플리케이션에서 전송합니다. 이를 통해 애플리케이션은 데이터 집약적인 작업이 필요할 때까지 연기할 수 있습니다. 예를 들어 애플리케이션은 연락처의 고해상도 사진과 최신 소셜 스트림 항목을 읽고 표시하여 이 알림에 응답할 수 있습니다. 이 기능은 소셜 스트림 상호작용 섹션에 자세히 설명되어 있습니다.
viewGroupActivity
- 그룹 정보를 표시할 수 있는 애플리케이션의 활동의 정규화된 클래스 이름입니다. 사용자가 기기의 연락처 애플리케이션에서 그룹 라벨을 클릭하면 이 활동의 UI가 표시됩니다.
viewGroupActionLabel
-
사용자가 애플리케이션에서 그룹을 볼 수 있는 UI 컨트롤에 연락처 애플리케이션이 표시하는 라벨입니다.
이 특성에서는 문자열 리소스 식별자가 허용됩니다.
viewStreamItemActivity
- 사용자가 원시 연락처의 스트림 항목을 클릭할 때 기기의 연락처 애플리케이션에서 실행하는 애플리케이션의 활동의 정규화된 클래스 이름입니다.
viewStreamItemPhotoActivity
- 사용자가 원시 연락처의 스트림 항목에서 사진을 클릭할 때 기기의 연락처 애플리케이션이 실행하는 애플리케이션의 활동의 정규화된 클래스 이름입니다.
<ContactsDataKind> 요소
<ContactsDataKind>
요소는 연락처 애플리케이션의 UI에서 애플리케이션의 맞춤 데이터 행 표시를 제어합니다. 이 요소에는 다음 구문이 있습니다.
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
포함된 위치:
<ContactsAccountType>
설명:
이 요소를 사용하면 연락처 애플리케이션이 원시 연락처의 세부정보에 맞춤 데이터 행의 콘텐츠를 표시하도록 할 수 있습니다. <ContactsAccountType>
의 각 <ContactsDataKind>
하위 요소는 동기화 어댑터가 ContactsContract.Data
테이블에 추가하는 맞춤 데이터 행의 유형을 나타냅니다. 사용하는 맞춤 MIME 유형마다 <ContactsDataKind>
요소를 하나씩 추가합니다. 데이터를 표시하지 않을 맞춤 데이터 행이 있는 경우 요소를 추가할 필요가 없습니다.
속성:
android:mimeType
-
ContactsContract.Data
테이블의 맞춤 데이터 행 유형 중 하나에 대해 정의한 맞춤 MIME 유형입니다. 예를 들어vnd.android.cursor.item/vnd.example.locationstatus
값은 연락처의 마지막으로 알려진 위치를 기록하는 데이터 행의 맞춤 MIME 유형일 수 있습니다. android:icon
- 연락처 애플리케이션이 데이터 옆에 표시하는 Android 드로어블 리소스입니다. 이를 사용하여 사용자에게 데이터가 서비스에서 가져온 것임을 나타냅니다.
android:summaryColumn
- 데이터 행에서 가져온 두 값 중 첫 번째 값의 열 이름입니다. 이 값은 이 데이터 행의 항목의 첫 번째 줄로 표시됩니다. 첫 번째 줄은 데이터의 요약으로 사용되도록 고안되었지만 선택사항입니다. android:detailColumn도 참고하세요.
android:detailColumn
-
데이터 행에서 검색된 두 값 중 두 번째 값의 열 이름입니다. 이 값은 이 데이터 행의 항목의 두 번째 줄로 표시됩니다.
android:summaryColumn
도 참고하세요.
연락처 제공자 추가 기능
이전 섹션에 설명된 기본 기능 외에도 연락처 제공자는 연락처 데이터를 사용하는 데 유용한 다음과 같은 기능을 제공합니다.
- 연락처 그룹
- 사진 기능
연락처 그룹
연락처 제공자는 원하는 경우 관련 연락처 모음에 그룹 데이터로 라벨을 지정할 수 있습니다. 사용자 계정과 연결된 서버에서 그룹을 유지하려면 계정의 계정 유형에 맞는 동기화 어댑터가 연락처 제공업체와 서버 간에 그룹 데이터를 전송해야 합니다. 사용자가 서버에 새 연락처를 추가한 다음 이 연락처를 새 그룹에 추가하면 동기화 어댑터는 새 그룹을 ContactsContract.Groups
테이블에 추가해야 합니다. 원시 연락처가 속한 그룹은 ContactsContract.CommonDataKinds.GroupMembership
MIME 유형을 사용하여 ContactsContract.Data
테이블에 저장됩니다.
서버의 원시 연락처 데이터를 연락처 제공업체에 추가하는 동기화 어댑터를 설계하고 있으며 그룹을 사용하지 않는 경우 제공업체에 데이터를 표시하도록 지시해야 합니다. 사용자가 기기에 계정을 추가할 때 실행되는 코드에서 연락처 제공업체가 계정에 추가하는 ContactsContract.Settings
행을 업데이트합니다. 이 행에서 Settings.UNGROUPED_VISIBLE
열의 값을 1로 설정합니다. 이렇게 하면 그룹을 사용하지 않더라도 연락처 제공자가 항상 연락처 데이터를 표시합니다.
연락처 사진
ContactsContract.Data
테이블은 사진을 MIME 유형이 Photo.CONTENT_ITEM_TYPE
인 행으로 저장합니다. 행의 CONTACT_ID
열은 속해 있는 원시 연락처의 _ID
열에 연결됩니다.
ContactsContract.Contacts.Photo
클래스는 연락처의 기본 원시 연락처의 기본 사진인 연락처의 기본 사진에 관한 사진 정보를 포함하는 ContactsContract.Contacts
의 하위 테이블을 정의합니다. 마찬가지로 ContactsContract.RawContacts.DisplayPhoto
클래스는 원시 연락처의 기본 사진에 대한 사진 정보를 포함하는 ContactsContract.RawContacts
의 하위 테이블을 정의합니다.
ContactsContract.Contacts.Photo
및 ContactsContract.RawContacts.DisplayPhoto
의 참조 문서에는 사진 정보를 검색하는 예가 포함되어 있습니다. 원시 연락처의 기본 썸네일을 가져오는 편의 클래스는 없지만 ContactsContract.Data
테이블에 쿼리를 전송하여 원시 연락처의 _ID
, Photo.CONTENT_ITEM_TYPE
, IS_PRIMARY
열을 선택하여 원시 연락처의 기본 사진 행을 찾을 수 있습니다.
한 사람의 소셜 스트림 데이터에도 사진이 포함되어 있을 수 있습니다. 이러한 사진은 android.provider.ContactsContract.StreamItemPhotos 테이블에 저장되며, 이 테이블에 대해서는 소셜 스트림 사진 섹션에서 자세히 설명합니다.