The Android Developer Challenge is back! Submit your idea before December 2.

Mengambil detail untuk kontak

Pelajaran ini menjelaskan cara mengambil data detail untuk kontak, seperti alamat email, nomor telepon, dan sebagainya. Itulah detail yang dicari pengguna saat mereka mengambil kontak. Anda dapat memberikan semua detail kontak, atau hanya menampilkan detail jenis tertentu, seperti alamat email.

Langkah-langkah dalam pelajaran ini mengasumsikan bahwa Anda sudah memiliki baris ContactsContract.Contacts untuk kontak yang dipilih pengguna. Pelajaran Mengambil nama kontak menunjukkan cara mengambil daftar kontak.

Mengambil semua detail kontak

Untuk mengambil semua detail kontak, telusuri tabel ContactsContract.Data untuk setiap baris yang berisi LOOKUP_KEY kontak. Kolom ini tersedia di tabel ContactsContract.Data karena Penyedia Kontak membuat gabungan implisit antara tabel ContactsContract.Contacts dan tabel ContactsContract.Data. Kolom LOOKUP_KEY dijelaskan secara lebih terperinci dalam pelajaran Mengambil nama kontak.

Catatan: Mengambil semua detail untuk sebuah kontak dapat menurunkan performa perangkat karena perangkat harus mengambil semua kolom dalam tabel ContactsContract.Data. Pertimbangkan dampaknya terhadap performa sebelum menggunakan teknik ini.

Meminta izin

Untuk membaca dari Penyedia Kontak, aplikasi Anda harus memiliki izin READ_CONTACTS. Untuk meminta izin ini, tambahkan elemen turunan <manifest> berikut ke file manifes Anda:

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

Menyiapkan proyeksi

Tergantung jenis data yang dimuat oleh baris, data mungkin menggunakan sedikit atau banyak kolom. Selain itu, data berada di kolom yang berbeda-beda tergantung jenis datanya. Untuk memastikan Anda mendapatkan semua kolom yang mungkin untuk semua jenis data yang mungkin, Anda perlu menambahkan semua nama kolom ke proyeksi Anda. Selalu ambil Data._ID jika Anda mengaitkan hasil Cursor ke ListView; jika tidak, binding tidak akan berfungsi. Juga ambil Data.MIMETYPE agar Anda dapat mengidentifikasi jenis data dari setiap baris yang Anda ambil. Contoh:

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
    )
    

Java

        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
                };
    

Proyeksi ini mengambil semua kolom untuk sebuah baris dalam tabel ContactsContract.Data, menggunakan nama kolom yang ditentukan dalam class ContactsContract.Data.

Jika ingin, Anda juga dapat menggunakan konstanta kolom lain yang ditentukan di atau diwarisi oleh class ContactsContract.Data. Namun, perlu diperhatikan bahwa kolom SYNC1 hingga SYNC4 diperuntukkan bagi adaptor sinkronisasi, sehingga datanya tidak berguna.

Menentukan kriteria pemilihan

Tetapkan konstanta untuk klausa pemilihan, array untuk menampung argumen pemilihan, dan variabel untuk menampung nilai pemilihan. Gunakan kolom Contacts.LOOKUP_KEY untuk menemukan kontak. Contoh:

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
    

Java

        // 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
    

Penggunaan "?" sebagai placeholder dalam ekspresi teks pemilihan memastikan bahwa spesifikasi penelusuran dihasilkan melalui binding, bukan kompilasi SQL. Pendekatan ini menghilangkan kemungkinan injeksi SQL yang berbahaya.

Menentukan urutan penyortiran

Tetapkan urutan penyortiran yang Anda inginkan dalam Cursor yang dihasilkan. Untuk menyatukan semua baris untuk jenis data tertentu, sortir berdasarkan Data.MIMETYPE. Argumen kueri ini mengelompokkan semua baris email menjadi satu, semua baris telepon menjadi satu, dan seterusnya. Contoh:

Kotlin

    /*
     * Defines a string that specifies a sort order of MIME type
     */
    private const val SORT_ORDER = ContactsContract.Data.MIMETYPE
    

Java

        /*
         * Defines a string that specifies a sort order of MIME type
         */
        private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;
    

Catatan: Beberapa jenis data tidak menggunakan subjenis, jadi Anda tidak dapat menyortir menurut subjenis. Sebagai gantinya, Anda harus mengiterasi Cursor yang ditampilkan, menentukan jenis data baris saat ini, dan menyimpan data untuk baris yang menggunakan subjenis. Setelah selesai membaca cursor, selanjutnya Anda dapat menyortir setiap jenis data menurut subjenis dan menampilkan hasilnya.

Menginisialisasi loader

Selalu lakukan pengambilan data dari Penyedia Kontak (dan semua penyedia konten lainnya) di thread latar belakang. Gunakan framework Loader yang ditentukan oleh class LoaderManager dan antarmuka LoaderManager.LoaderCallbacks untuk melakukan pengambilan data di latar belakang.

Setelah siap mengambil baris, inisialisasi framework loader dengan memanggil initLoader(). Teruskan ID integer ke metode; ID ini akan diteruskan ke metode LoaderManager.LoaderCallbacks. Dengan ID ini, Anda dapat menggunakan beberapa loader di sebuah aplikasi sehingga Anda dapat membedakan antara loader satu dengan lainnya.

Cuplikan berikut menunjukkan cara menginisialisasi framework loader:

Kotlin

    // Defines a constant that identifies the loader
    private const val DETAILS_QUERY_ID: Int = 0

    class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
        ...
        override fun onActivityCreated(savedInstanceState: Bundle?) {
            ...
            // Initializes the loader framework
            loaderManager.initLoader(DETAILS_QUERY_ID, null, this)
    

Java

    public class DetailsFragment extends Fragment implements
            LoaderManager.LoaderCallbacks<Cursor> {
        ...
        // Defines a constant that identifies the loader
        static int DETAILS_QUERY_ID = 0;
        ...
        /*
         * Invoked when the parent Activity is instantiated
         * and the Fragment's UI is ready. Put final initialization
         * steps here.
         */
        @Override
        onActivityCreated(Bundle savedInstanceState) {
            ...
            // Initializes the loader framework
            getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
    

Menerapkan onCreateLoader()

Terapkan metode onCreateLoader(), yang dipanggil oleh framework loader segera setelah Anda memanggil initLoader(). Tampilkan CursorLoader dari metode ini. Karena Anda menelusuri tabel ContactsContract.Data, gunakan konstanta Data.CONTENT_URI sebagai URI konten. Misalnya:

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
    }
    

Java

    @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
                        );
        }
    

Menerapkan onLoadFinished() dan onLoaderReset()

Terapkan metode onLoadFinished(). Framework loader ini memanggil onLoadFinished() saat Penyedia Kontak menampilkan hasil kueri. Contoh:

Kotlin

        override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
            when(loader.id) {
                DETAILS_QUERY_ID -> {
                    /*
                     * Process the resulting Cursor here.
                     */
                }
                ...
            }
        }
    

Java

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
            switch (loader.getId()) {
                case DETAILS_QUERY_ID:
                        /*
                         * Process the resulting Cursor here.
                         */
                    }
                    break;
                ...
            }
        }
    

Metode onLoaderReset() dipanggil ketika framework loader mendeteksi bahwa data yang mendukung Cursor telah berubah. Pada tahap ini, hapus semua referensi yang ada ke Cursor dengan menetapkannya ke nol. Jika tidak, framework loader tidak akan menghancurkan Cursor lama, dan kebocoran memori akan terjadi. Contoh:

Kotlin

        override fun onLoaderReset(loader: Loader<Cursor>) {
            when (loader.id) {
                DETAILS_QUERY_ID -> {
                    /*
                     * If you have current references to the Cursor,
                     * remove them here.
                     */
                }
                ...
            }
        }
    

Java

        @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;
        }
    

Mengambil detail spesifik untuk sebuah kontak

Pengambilan jenis data tertentu untuk sebuah kontak, misalnya semua email, mengikuti pola yang sama dengan pengambilan semua detail. Inilah satu-satunya perubahan yang perlu Anda buat pada kode yang tercantum di Mengambil semua detail untuk kontak:

Proyeksi
Ubah proyeksi Anda untuk mengambil kolom-kolom yang spesifik untuk jenis data itu. Ubah juga proyeksi untuk menggunakan konstanta nama kolom yang ditentukan dalam subclass ContactsContract.CommonDataKinds sesuai dengan jenis datanya.
Pemilihan
Ubah teks pemilihan untuk menelusuri nilai MIMETYPE yang spesifik untuk jenis data Anda.
Urutan penyortiran
Karena Anda hanya memilih satu jenis detail, jangan kelompokkan Cursor yang ditampilkan menurut Data.MIMETYPE.

Modifikasi ini dijelaskan di bagian berikut.

Menentukan proyeksi

Tentukan kolom yang ingin Anda ambil, menggunakan konstanta nama kolom dalam subclass ContactsContract.CommonDataKinds untuk jenis data itu. Jika Anda ingin mengaitkan Cursor ke ListView, pastikan untuk mengambil kolom _ID. Misalnya, untuk mengambil data email, tentukan proyeksi berikut:

Kotlin

    private val PROJECTION: Array<String> = arrayOf(
            ContactsContract.CommonDataKinds.Email._ID,
            ContactsContract.CommonDataKinds.Email.ADDRESS,
            ContactsContract.CommonDataKinds.Email.TYPE,
            ContactsContract.CommonDataKinds.Email.LABEL
    )
    

Java

        private static final String[] PROJECTION =
                {
                    ContactsContract.CommonDataKinds.Email._ID,
                    ContactsContract.CommonDataKinds.Email.ADDRESS,
                    ContactsContract.CommonDataKinds.Email.TYPE,
                    ContactsContract.CommonDataKinds.Email.LABEL
                };
    

Perhatikan bahwa proyeksi ini menggunakan nama kolom yang ditentukan dalam class ContactsContract.CommonDataKinds.Email, bukan nama kolom yang ditentukan dalam class ContactsContract.Data. Penggunaan nama kolom untuk email tertentu ini menjadikan kode lebih mudah dibaca.

Dalam proyeksi ini, Anda juga dapat menggunakan salah satu kolom lain yang ditentukan dalam subclass ContactsContract.CommonDataKinds.

Menentukan kriteria pemilihan

Tentukan ekspresi teks penelusuran yang mengambil baris untuk LOOKUP_KEY kontak tertentu dan Data.MIMETYPE dari detail yang Anda inginkan. Tempatkan nilai MIMETYPE di antara tanda kutip tunggal dengan menggabungkan karakter "'" (tanda kutip tunggal) ke awal dan akhir konstanta; jika tidak, penyedia akan mengartikan nilai itu sebagai nama variabel dan bukan sebagai nilai string. Anda tidak perlu menggunakan placeholder untuk nilai ini, karena Anda menggunakan konstanta, bukan nilai yang disediakan pengguna. Contoh:

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("")
    

Java

        /*
         * 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 = { "" };
    

Menentukan urutan penyortiran

Tetapkan urutan penyortiran untuk Cursor yang ditampilkan. Karena Anda mengambil jenis data tertentu, hilangkan penyortiran menurut MIMETYPE. Sebagai gantinya, jika jenis data detail yang Anda telusuri menyertakan subjenis, lakukan penyortiran menurut subjenis tersebut. Misalnya, untuk data email Anda dapat menyortir menurut Email.TYPE:

Kotlin

    private const val SORT_ORDER: String = "${Email.TYPE} ASC"
    

Java

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