Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

연락처 세부정보 검색

이 과정에서는 연락처와 관련된 세부적인 데이터(예: 이메일 주소, 전화번호 등)를 검색하는 방법에 관해 설명합니다. 이는 사용자가 연락처를 검색할 때 찾는 세부정보입니다. 사용자에게 연락처의 모든 세부정보를 제공하거나 이메일 주소와 같은 특정 유형의 세부정보만 표시할 수 있습니다.

이 과정의 단계에서는 사용자가 선택한 연락처의 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" />
    

프로젝션 설정

행에 포함된 데이터 유형에 따라 몇 개의 열만 사용하거나 많은 열을 사용할 수 있습니다. 또한 데이터 유형에 따라 데이터가 다른 열에 포함됩니다. 가능한 모든 데이터 유형과 관련해 가능한 모든 열을 가져오려면 프로젝션에 열 이름을 모두 추가해야 합니다. 결과 CursorListView에 바인딩하는 경우 항상 Data._ID를 검색합니다. 그렇지 않으면 결합이 작동하지 않습니다. 또한, 검색하는 각 행의 데이터 유형을 식별할 수 있도록 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 클래스에 정의되거나 상속된 다른 열 상수를 사용할 수도 있습니다. 하지만, SYNC4를 통한 SYNC1 열은 동기화 어댑터에서 사용하므로 이러한 열의 데이터는 유용하지 않습니다.

선택 기준 정의

선택 절을 나타내는 상수, 선택 인수를 보유할 배열, 선택 값을 저장할 변수를 정의하세요. 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 테이블을 검색하고 있으므로 콘텐츠 URI로 Data.CONTENT_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;
                ...
            }
        }
    

로더 프레임워크에서 결과 Cursor를 지원하는 데이터가 변경된 것을 감지하면 onLoaderReset() 메서드가 호출됩니다. 이때, 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 서브클래스에서 열 이름 상수를 사용하여 검색하려는 열을 정의합니다. CursorListView에 결합하려면 _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.Data 클래스에 정의된 열 이름 대신 ContactsContract.CommonDataKinds.Email 클래스에 정의된 열 이름을 사용합니다. 이메일 관련 열 이름을 사용하면 코드를 더 쉽게 읽을 수 있습니다.

프로젝션에서 ContactsContract.CommonDataKinds 서브클래스에 정의된 다른 열을 사용할 수도 있습니다.

선택 기준 정의

특정 연락처에서 원하는 세부정보의 LOOKUP_KEYData.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"
    

자바

        private static final String SORT_ORDER = Email.TYPE + " ASC ";