Android 培训

获取联系人详情

编写:spencer198711 - 原文:http://developer.android.com/training/contacts-provider/retrieve-details.html

这一课展示了如何取得一个联系人的详细信息,比如email地址、电话号码等。当使用者去获取联系人信息的时候,这些信息正是他们所查找的。你可以给他们关于一个联系人的所有信息,或者仅仅显示一个特定的数据类型,比如email地址。

这一课假设你已经获取到了一个用户所选取的联系人的ContactsContract.Contacts数据项。在获取联系人名字那一课展示了如何获取联系人列表。

1)获取联系人的所有详细信息

为了取得一个联系人的所有详情,需要去搜索ContactsContract.Data表,寻找包含联系人LOOKUP_KEY的任意一行。列名称可以从ContactsContract.Data表中查到。因为ContactsProvider隐示地连接了ContactsContract.Contacts表和ContactsContract.Data表。关于LOOKUP_KEY列,在获取联系人名字那一课有详细的描述。

Notes:取得一个联系人的所有信息会降低设备的性能,因为这需要获取ContactsContract.Data表的所有列,在你使用这种方法之前,请认真考虑对性能影响。

1.1)请求权限

为了能够读联系人数据库,你的应用必须拥有READ_CONTACTS权限,为了请求这个权限,需要在清单文件里边添加以下子节点

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

1.2)设置查询映射

根据一行数据的数据类型的不同,它可能会使用很多列或者只使用几列。另外,数据会根据不同的数据类型而在不同的列中。为了确保能够获取所有可能的数据类型的所有可能的数据列,需要在查询映射中添加所有列的名字。如果你要把Cursor绑定到ListView,记得永远都需要获取Data._ID,否则的话,界面绑定就不会起作用。同时你也需要获取Data.MIMETYPE列,这样你才能识别你获取到的每一行数据的数据类型。例如:

private static final String PROJECTION =
            {
                Data._ID,
                Data.MIMETYPE,
                Data.DATA1,
                Data.DATA2,
                Data.DATA3,
                Data.DATA4,
                Data.DATA5,
                Data.DATA6,
                Data.DATA7,
                Data.DATA8,
                Data.DATA9,
                Data.DATA10,
                Data.DATA11,
                Data.DATA12,
                Data.DATA13,
                Data.DATA14,
                Data.DATA15
            };

这个查询映射使用了 ContactsContract.Data类中定义的列名字,获取了ContactsContract.Data表的所有数据列。

你也可以使用由ContactsContract.Data或其子类定义的列常量去设置查询映射。需要注意的是,从SYNC1到SYNC4的数据列是sync adapter同步数据所使用的,它们的值对我们没有意义。

1.3)定义查询标准

为查询选择子句定义一个常量,一个包含查询选择参数的数组,以及一个保存查询选择值的变量。使用数据列LOOKUP_KEY去查找这个联系人。例如:

    // Defines the selection clause
    private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
    // Defines the array to hold the search criteria
    private String[] mSelectionArgs = { "" };
    /*
     * 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 String mLookupKey;

在查询选择表达式中使用 “?”占位符,确保了搜索是由绑定生成而不是由SQL编译生成。这种方法消除了恶意SQL注入的可能性。

1.4)定义排序顺序

定义在查询结果Cursor中希望的排序顺序。为了让特定类型的数据列聚集在一起,需要按照Data.MIMETYPE去排序。这种形式的查询排序参数让所有的email信息排在一起,所有的电话信息排在一起等等。例如:

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

Notes:一些数据类型不使用子类型,所以你不能按照子类型来排序。作为替代方法,你不得不遍历返回的cursor,去判定当前行的数据类型,为那些使用子类型的数据行保存数据。当读取完cursor后,你可以根据子类型去排序每一个数据类型,然后显示结果。

1.5)初始化查询loader

永远在后台线程中去从Contacts Provider中获取数据(或者其他的content provider)。使用loader框架中的LoaderManager类和LoaderManager.LoaderCallbacks在后台去做获取数据的工作。

当你已经准备好了去获取数据行,需要通过调用initLoader()方法去初始化loader框架。传递一个Integer类型的标示符给initLoader()方法,这个标示符会传递给LoaderManager.LoaderCallbacks的相关方法。当在一个应用中使用多个loader时,这个标示符能够帮助你区分它们。

以下的代码片段展示了如何初始化loader框架:

public class DetailsFragment extends Fragment implements
        LoaderManager.LoaderCallbacks<Cursor> {
    ...
    // Defines a constant that identifies the loader
    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);

1.6)实现onCreateLoader方法

实现onCreateLoader方法。loader框架会在你调用initLoader()方法后立即调用onCreateLoader方法,并返回一个CursorLoader对象。由于你是要搜索ContactsContract.Data表,所以需要使用常量Data.CONTENT_URI作为内容URI。例如:

    @Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        // Choose the proper action
        switch (loaderId) {
            case DETAILS_QUERY_ID:
            // Assigns the selection parameter
            mSelectionArgs[0] = mLookupKey;
            // Starts the query
            CursorLoader mLoader =
                    new CursorLoader(
                            getActivity(),
                            Data.CONTENT_URI,
                            PROJECTION,
                            SELECTION,
                            mSelectionArgs,
                            SORT_ORDER
                    );
            ...
    }

1.7)实现onLoadFinished()方法和onLoaderReset()方法

实现onLoadFinished()方法。当Contacts Provider返回查询结果的时候,loader框架会调用onLoadFinished()方法。例如:

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

当loader框架检测到结果集Cursor所对应的数据已经发生变化的时候,会调用onLoaderReset()方法。这时,需要通过把Cursor设置为null来移除对已经存在Cursor对象的引用,如果不这样做的话,loader框架就不会销毁旧的Cursor对象,就回发生内存泄漏。例如:

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

2)获取联系人的特定类型的信息

获取联系人的特定类型的信息,例如所有的email信息,跟获取联系人的所有详细信息类似。你仅需要修改在获取联系人的所有详细信息列举的以下部分代码:

  • 映射

    修改查询映射使得能够针对特定类型的数据查询。同时需要修改查询映射的列名称,要根据查询的数据类型使用在ContactsContract.CommonDataKinds的子类中定义的常量。

  • 查询选择

    根据查询的数据类型,修改查询选择子句去搜索特定类型的MIMETYPE值。

  • 排序顺序

    由于仅仅搜索一种类型的详细数据,所以不需要根据返回的Cursor的Data.MIMETYPE去将结果分组。

这些修改将会在以下章节中详细描述。

2.1)设置查询映射

使用ContactsContract.CommonDataKinds的特定类型子类所定义的列名称常量,定义你想要获取的数据列。如果你打算把结果Cursor绑定到ListView,确保要获取_ID列。例如,为了获取email数据,需要定义以下数据映射:

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

需要注意的是,这个查询映射使用了在ContactsContract.CommonDataKinds.Email类中定义的列名称,替代了ContactsContract.Data类中定义的列名称。使用email类型特定的列名称使得代码更具可读性。

在查询投影中,你还可以使用其他ContactsContract.CommonDataKinds子类所定义的数据列。

2.2)定义查询标准

根据你想要找的特定联系人的LOOKUP_KEY和联系人详细信息的Data.MIMETYPE定义一个搜索表达式,去获取数据。把MIMETYPE的值从头到尾用单引号括住,否则的话,content provider将会把这个常量翻译成变量名,而不是翻译成字符串值。不需要为这个值使用占位符,因为你使用的是常量,而不是用户提供的值。例如:

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

2.3)定义排序规则

为查询返回的Cursor定义一个排序规则。由于是为了获取特定类型的数据,请忽略在MIMETYPE上的排序。作为替代,如果查询的详细数据类型包含子类型,可以根据这个子类型去排序。例如,对于email数据你可以根据Email.TYPE排序:

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