بازیابی جزئیات مخاطب

این درس نشان می‌دهد که چگونه داده‌های جزئیات مخاطب، مثل نشانی‌های ایمیل، شماره‌های تلفن، و غیره را بازیابی کنید. این‌ها جزئیاتی هستند که کاربران هنگام بازیابی مخاطب به‌دنبال آن‌ها می‌گردند. می‌توانید همه جزئیات مخاطب را به آن‌ها بدهید یا فقط جزئیات نوع خاصی را نمایش دهید، مثلاً نشانی‌های ایمیل.

مراحل این درس فرض می‌کند که ازقبل ContactsContract.Contacts ردیف برای مخاطبی که کاربر انتخاب کرده است دارید. درس بازیابی نام مخاطبین نحوه بازیابی فهرست مخاطبین را نشان می‌دهد.

بازیابی همه جزئیات مخاطب

برای بازیابی همه جزئیات مخاطب، جدول ContactsContract.Data را برای هر ردیفی که حاوی LOOKUP_KEY مخاطب است جستجو کنید. این ستون در جدول ContactsContract.Data دردسترس است، زیرا «ارائه‌دهنده مخاطبین» بین جدول ContactsContract.Contacts و جدول ContactsContract.Data پیوند ضمنی ایجاد می‌کند. ستون LOOKUP_KEY با جزئیات بیشتر در درس بازیابی نام مخاطبین شرح داده شده است.

توجه: بازیابی همه جزئیات مخاطب عملکرد دستگاه را کاهش می‌دهد، زیرا باید همه ستون‌های جدول ContactsContract.Data را بازیابی کند. قبل‌از استفاده از این تکنیک، تأثیر آن بر عملکرد را درنظر بگیرید.

درخواست اجازه

برای خواندن از «ارائه‌دهنده مخاطبین»، برنامه‌تان باید اجازه READ_CONTACTS را داشته باشد. برای درخواست این اجازه، عنصر فرزند زیر را به فایل مانیفست خود اضافه کنید: <manifest>

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

راه‌اندازی پیش‌بینی

بسته به نوع داده‌ای که ردیف دارد، ممکن است فقط از چند ستون یا از ستون‌های زیادی استفاده کند. علاوه‌براین، داده‌ها بسته به نوع داده در ستون‌های مختلفی قرار دارند. برای اطمینان از اینکه همه ستون‌های ممکن را برای همه انواع داده ممکن دریافت می‌کنید، باید همه نام‌های ستون را به پیش‌بینی‌تان اضافه کنید. اگر نتیجه را Cursor به ListView پیوند می‌دهید، همیشه Data._ID را بازیابی کنید؛ درغیراین‌صورت، پیوند کار نخواهد کرد. همچنین Data.MIMETYPE را بازیابی کنید تا بتوانید نوع داده هر ردیفی را که بازیابی می‌کنید شناسایی کنید. برای مثال:

کاتلین

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 برای پیدا کردن مخاطب استفاده کنید. برای مثال:

کاتلین

// 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 مرتب کنید. این آرگومان پُرسمان همه ردیف‌های ایمیل را باهم، همه ردیف‌های تلفن را باهم، و غیره گروه‌بندی می‌کند. برای مثال:

کاتلین

/*
 * 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 برگردانده‌شده را تکرار کنید، نوع داده ردیف فعلی را تعیین کنید، و داده‌ها را برای ردیف‌هایی که از نوع فرعی استفاده می‌کنند ذخیره کنید. وقتی خواندن مکان‌نما را تمام کردید، می‌توانید هر نوع داده را براساس نوع فرعی مرتب کنید و نتایج را نمایش دهید.

مقداردهی اولیه «بارکننده»

همیشه بازیابی‌ها را از «ارائه‌دهنده مخاطبین» (و همه ارائه‌دهندگان محتوای دیگر) در رشته پس‌زمینه انجام دهید. از چارچوب Loader تعریف‌شده توسط کلاس LoaderManager و میانای LoaderManager.LoaderCallbacks برای بازیابی‌های پس‌زمینه استفاده کنید.

وقتی آماده بازیابی ردیف‌ها هستید، چارچوب بارکننده را با فراخوانی initLoader() مقداردهی اولیه کنید. یک شناسه عدد صحیح را به روش ارسال کنید؛ این شناسه به روش‌های LoaderManager.LoaderCallbacks ارسال می‌شود. شناسه به شما کمک می‌کند با ایجاد امکان تمایز بین بارکننده‌ها، از چند بارکننده در برنامه استفاده کنید.

تکه‌کد زیر نحوه مقداردهی اولیه چارچوب بارکننده را نشان می‌دهد:

کاتلین

// 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 به‌عنوان نشانی وب محتوا استفاده کنید. برای مثال:

کاتلین

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() را وقتی «ارائه‌دهنده مخاطبین» نتایج پُرسمان را برمی‌گرداند فرا می‌خواند. برای مثال:

کاتلین

    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 قدیمی را ازبین نمی‌برد و با نشت حافظه مواجه خواهید شد. برای مثال:

کاتلین

    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 که مختص نوع داده شما است اصلاح کنید.
گزینه‌های مرتب‌سازی
چون فقط یک نوع جزئیات را انتخاب می‌کنید، Cursor برگردانده‌شده را براساس Data.MIMETYPE گروه‌بندی نکنید.

این اصلاحات در بخش‌های زیر شرح داده شده است.

تعریف کردن تصویرسازی

ستون‌هایی را که می‌خواهید بازیابی کنید بااستفاده از ثابت‌های نام ستون در زیرکلاس ContactsContract.CommonDataKinds برای نوع داده تعریف کنید. اگر قصد دارید Cursor خود را به ListView پیوند دهید، حتماً ستون _ID را بازیابی کنید. برای مثال، برای بازیابی داده‌های ایمیل، برنامه پیش‌بینی زیر را تعریف کنید:

کاتلین

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 را با الحاق نویسه «'» (گیومه تکی) به ابتدا و انتهای ثابت، در گیومه تکی قرار دهید؛ درغیراین‌صورت، ارائه‌دهنده ثابت را به‌عنوان نام متغیر تفسیر می‌کند نه به‌عنوان مقدار رشته. برای این مقدار نیازی به استفاده از جای‌بان ندارید، زیرا به‌جای مقدار ارائه‌شده توسط کاربر از مقدار ثابت استفاده می‌کنید. برای مثال:

کاتلین

/*
 * 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 مرتب کنید:

کاتلین

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

جاوا

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