이 과정에서는 연락처와 관련된 세부적인 데이터(예: 이메일 주소, 전화번호 등)를 검색하는 방법을 설명합니다. 이는 사용자가 연락처를 검색할 때 찾는 세부정보입니다. 연락처의 모든 세부정보를 제공할 수도 있고 특정 유형의 세부정보만 표시할 수도 있습니다. 정보를 공유할 수 있습니다.
이 과정의 단계에서는 사용자가 선택한 연락처의 ContactsContract.Contacts
행이 이미 있다고 가정합니다.
연락처 이름 검색 과정에서는
연락처 목록 검색
연락처 관련 모든 세부정보 검색
연락처의 모든 세부정보를 검색하려면
ContactsContract.Data
연락처의
LOOKUP_KEY
입니다. 이 열은 연락처 제공자가 ContactsContract.Contacts
테이블과 ContactsContract.Data
테이블 사이에 암시적 조인을 실행하므로 ContactsContract.Data
테이블에서 사용할 수 있습니다. LOOKUP_KEY
열은 연락처 이름 검색 과정에서 자세히 설명합니다.
참고: 연락처의 모든 세부정보를 검색하면 ContactsContract.Data
테이블의 모든 열을 검색해야 하므로 기기의 성능이 저하됩니다. 이 기법을 사용하기 전에 성능에 미칠 영향을 고려하세요.
권한 요청
연락처 제공자에서 읽으려면 앱에 다음이 있어야 합니다.
READ_CONTACTS
권한
이 권한을 요청하려면
<manifest>
를 매니페스트 파일에 추가합니다.
<uses-permission android:name="android.permission.READ_CONTACTS" />
프로젝션 설정
행에 포함된 데이터 유형에 따라 몇 개의 열만 사용하거나 많은 열을 사용할 수 있습니다. 또한
데이터 유형에 따라 데이터가 다른 열에 있음을 알 수 있습니다.
가능한 모든 데이터 유형과 관련해 가능한 모든 열을 가져오려면 프로젝션에 열 이름을 모두 추가해야 합니다. 항상 검색
결과를 바인딩하는 경우 Data._ID
Cursor
을 ListView
로 그렇지 않으면 바인딩이
작동하지 않습니다. 또한, 검색하는 각 행의 데이터 유형을 식별할 수 있도록 Data.MIMETYPE
을 검색합니다. 예를 들면 다음과 같습니다.
Kotlin
private val PROJECTION: Array<out String> = arrayOf( ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA3, ContactsContract.Data.DATA4, ContactsContract.Data.DATA5, ContactsContract.Data.DATA6, ContactsContract.Data.DATA7, ContactsContract.Data.DATA8, ContactsContract.Data.DATA9, ContactsContract.Data.DATA10, ContactsContract.Data.DATA11, ContactsContract.Data.DATA12, ContactsContract.Data.DATA13, ContactsContract.Data.DATA14, ContactsContract.Data.DATA15 )
자바
private static final String[] PROJECTION = { ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA3, ContactsContract.Data.DATA4, ContactsContract.Data.DATA5, ContactsContract.Data.DATA6, ContactsContract.Data.DATA7, ContactsContract.Data.DATA8, ContactsContract.Data.DATA9, ContactsContract.Data.DATA10, ContactsContract.Data.DATA11, ContactsContract.Data.DATA12, ContactsContract.Data.DATA13, ContactsContract.Data.DATA14, ContactsContract.Data.DATA15 };
이 투영법은
ContactsContract.Data
테이블은
ContactsContract.Data
클래스
선택적으로,
ContactsContract.Data
클래스. 하지만 열은
SYNC1
~
SYNC4
는 동기화에 사용됩니다.
어댑터의 데이터가 유용하지 않습니다.
선택 기준 정의
선택 절의 상수, 선택 인수를 보유할 배열,
변수를 사용하여 선택 값을 보유합니다. 사용
Contacts.LOOKUP_KEY
열을 사용하여
연락처를 찾습니다. 예를 들면 다음과 같습니다.
Kotlin
// Defines the selection clause private const val SELECTION: String = "${ContactsContract.Data.LOOKUP_KEY} = ?" ... // Defines the array to hold the search criteria private val selectionArgs: Array<String> = arrayOf("") /* * Defines a variable to contain the selection value. Once you * have the Cursor from the Contacts table, and you've selected * the desired row, move the row's LOOKUP_KEY value into this * variable. */ private var lookupKey: String? = null
자바
// Defines the selection clause private static final String SELECTION = Data.LOOKUP_KEY + " = ?"; // Defines the array to hold the search criteria private String[] selectionArgs = { "" }; /* * Defines a variable to contain the selection value. Once you * have the Cursor from the Contacts table, and you've selected * the desired row, move the row's LOOKUP_KEY value into this * variable. */ private lateinit var lookupKey: String
선택 텍스트 표현식에 자리표시자로 '?'를 사용하면 SQL 컴파일 대신 바인딩을 통해 검색이 생성됩니다. 이 접근 방식은 악성 SQL 삽입 가능성이 있습니다.
정렬 순서 정의
결과 Cursor
에서 원하는 정렬 순서를 정의합니다. 받는사람
특정 데이터 유형의 모든 행을 함께 유지하고
Data.MIMETYPE
이 쿼리 인수는 모든 이메일 행, 모든 전화 행 등을 그룹화합니다. 예를 들면 다음과 같습니다.
Kotlin
/* * Defines a string that specifies a sort order of MIME type */ private const val SORT_ORDER = ContactsContract.Data.MIMETYPE
자바
/* * Defines a string that specifies a sort order of MIME type */ private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;
참고: 일부 데이터 유형은 하위유형을 사용하지 않으므로 하위유형별로 정렬할 수 없습니다.
대신 반환된 Cursor
를 반복해야 하며 현재 행의 데이터 유형을 결정하고 하위유형을 사용하는 행의 데이터를 저장해야 합니다. 커서 읽기 작업을 마치고 나면 하위유형별로 각 데이터 유형을 정렬하고 결과를 표시할 수 있습니다.
로더 초기화
항상 백그라운드 스레드의 연락처 제공자(및 기타 모든 콘텐츠 제공자)에서 검색합니다.
LoaderManager
클래스 및
백그라운드 작업을 위한 LoaderManager.LoaderCallbacks
인터페이스
가져올 수 있습니다.
행을 검색할 준비가 되면 initLoader()
를 호출하여 로더 프레임워크를 초기화합니다. 정수 식별자를 메서드에 전달합니다. 이 식별자는 LoaderManager.LoaderCallbacks
메서드로 전달됩니다. 식별자를 사용하면
로더를 구별할 수 있도록 하여 앱에서 여러 로더를 사용할 수 있습니다.
다음 스니펫은 로더 프레임워크를 초기화하는 방법을 보여줍니다.
Kotlin
// Defines a constant that identifies the loader private const val DETAILS_QUERY_ID: Int = 0 class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> { ... override fun onCreate(savedInstanceState: Bundle?) { ... // Initializes the loader framework loaderManager.initLoader(DETAILS_QUERY_ID, null, this)
자바
public class DetailsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { ... // Defines a constant that identifies the loader static int DETAILS_QUERY_ID = 0; ... @Override public void onCreate(Bundle savedInstanceState) { ... // Initializes the loader framework getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
onCreateLoader() 구현
onCreateLoader()
메서드를 구현합니다. 이 메서드는 호출 직후 로더 프레임워크에서 호출합니다.
initLoader()
입니다. 다음을 반환:
CursorLoader
를 호출합니다. 검색 중인 항목
ContactsContract.Data
테이블의 경우 상수를 사용합니다.
Data.CONTENT_URI
를 콘텐츠 URI로 사용합니다.
예를 들면 다음과 같습니다.
Kotlin
override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> { // Choose the proper action mLoader = when(loaderId) { DETAILS_QUERY_ID -> { // Assigns the selection parameter selectionArgs[0] = lookupKey // Starts the query activity?.let { CursorLoader( it, ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, SORT_ORDER ) } } ... } return mLoader }
자바
@Override public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { // Choose the proper action switch (loaderId) { case DETAILS_QUERY_ID: // Assigns the selection parameter selectionArgs[0] = lookupKey; // Starts the query CursorLoader mLoader = new CursorLoader( getActivity(), ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, SORT_ORDER ); }
onLoadFinished() 및 onLoaderReset() 구현
onLoadFinished()
메서드를 구현합니다. 로더 프레임워크는
onLoadFinished()
연락처 제공자가 쿼리 결과를 반환할 때 예를 들면 다음과 같습니다.
Kotlin
override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) { when(loader.id) { DETAILS_QUERY_ID -> { /* * Process the resulting Cursor here. */ } ... } }
자바
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { switch (loader.getId()) { case DETAILS_QUERY_ID: /* * Process the resulting Cursor here. */ } break; ... } }
onLoaderReset()
메서드는 로더 프레임워크에서 결과 Cursor
를 지원하는 데이터가 변경된 것을 감지하면 호출됩니다. 이때 Cursor
를 참조하는 모든 기존 항목을 null로 설정하여 삭제합니다. 그렇게 하지 않으면 로더 프레임워크에서 이전 Cursor
를 삭제하지 않아서 메모리 누수가 발생합니다. 예를 들면 다음과 같습니다.
Kotlin
override fun onLoaderReset(loader: Loader<Cursor>) { when (loader.id) { DETAILS_QUERY_ID -> { /* * If you have current references to the Cursor, * remove them here. */ } ... } }
자바
@Override public void onLoaderReset(Loader<Cursor> loader) { switch (loader.getId()) { case DETAILS_QUERY_ID: /* * If you have current references to the Cursor, * remove them here. */ } break; }
연락처 관련 특정 세부정보 검색
연락처의 특정 데이터 유형(예: 모든 이메일)을 검색할 때는 동일한 패턴을 따릅니다. 모든 세부정보를 가져오는 것과 같습니다 다음 코드만 변경하면 됩니다. 연락처 모든 세부정보 검색에 나열되어 있습니다.
- 프로젝션
-
투영을 수정하여
데이터 유형입니다. 또한 데이터 유형에 해당하는
ContactsContract.CommonDataKinds
서브클래스에 정의된 열 이름 상수를 사용하도록 프로젝션을 수정합니다. - 선택
-
선택 텍스트를 수정하여
다음에 해당하는
MIMETYPE
값 확인할 수 있습니다 - 정렬 순서
-
하나의 세부정보 유형만 선택하므로 반환된 세부정보는 그룹화하지 마세요.
Data.MIMETYPE
별Cursor
이러한 수정 사항은 다음 섹션에 설명되어 있습니다.
프로젝션 정의
서브클래스에서 열 이름 상수를 사용하여 검색하려는 열을 정의합니다.
데이터 유형의 경우 ContactsContract.CommonDataKinds
입니다.
Cursor
를 ListView
에 바인딩하려는 경우 다음 안내를 따르세요.
_ID
열을 가져와야 합니다. 예를 들어 이메일 데이터를 검색하려면 다음 프로젝션을 정의하세요.
Kotlin
private val PROJECTION: Array<String> = arrayOf( ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL )
자바
private static final String[] PROJECTION = { ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL };
이 프로젝션은 클래스에 정의된 열 이름을 사용합니다.
열 이름 대신 ContactsContract.CommonDataKinds.Email
사용
클래스 ContactsContract.Data
에 정의되어 있습니다. 이메일 관련 열 이름을 사용하면 코드를 더 쉽게 읽을 수 있습니다.
프로젝션에서는
ContactsContract.CommonDataKinds
서브클래스.
선택 기준 정의
특정 연락처의 행을 검색하는 검색 텍스트 표현식을 정의합니다.
LOOKUP_KEY
및
세부정보 Data.MIMETYPE
있습니다. 상수의 시작과 끝에 ''
'(작은따옴표)를 붙여 작은따옴표로 MIMETYPE
값을 묶습니다. 이렇게 하지 않으면, 제공자가 상수를 문자열 값이 아닌 변수 이름으로 해석합니다. 이 값에는 자리표시자를 사용할 필요가 없습니다.
사용자가 제공한 값이 아닌 상수를 사용합니다. 예를 들면 다음과 같습니다.
Kotlin
/* * Defines the selection clause. Search for a lookup key * and the Email MIME type */ private const val SELECTION = "${ContactsContract.Data.LOOKUP_KEY} = ? AND " + "${ContactsContract.Data.MIMETYPE} = '${Email.CONTENT_ITEM_TYPE}'" ... // Defines the array to hold the search criteria private val selectionArgs: Array<String> = arrayOf("")
자바
/* * Defines the selection clause. Search for a lookup key * and the Email MIME type */ private static final String SELECTION = Data.LOOKUP_KEY + " = ?" + " AND " + Data.MIMETYPE + " = " + "'" + Email.CONTENT_ITEM_TYPE + "'"; // Defines the array to hold the search criteria private String[] selectionArgs = { "" };
정렬 순서 정의
반환된 Cursor
의 정렬 순서를 정의합니다. 특정 데이터 유형을 검색 중이므로 MIMETYPE
에서 정렬을 생략합니다.
대신, 검색 중인 세부 데이터 유형에 하위유형이 포함된 경우 정렬하세요.
예를 들어 이메일 데이터의 경우
Email.TYPE
:
Kotlin
private const val SORT_ORDER: String = "${Email.TYPE} ASC"
Java
private static final String SORT_ORDER = Email.TYPE + " ASC ";