为什么我的联系人查询给出了具有不同 ID 的联系人?

Why is my contacts query giving a contact with a different id?

我有一个 Activity 允许用户 select 联系人。完成后,selected 联系人的 ID 将在回复 Intent 中传递。在处理结果的地方,要读取 selected 联系人的详细信息。 问题在于,在处理结果时,读取的详细信息与 select 编辑的联系人不同。 我不知道为什么。在我的例子中,用户只有 select 一个联系人。

我已经阅读了大量文档和其他有关阅读联系人的问题,但没有看到任何对我的案例有帮助的内容。

从 Activity 获取用户 selection 来自联系人:

@Override
protected void onCreate (Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_select_from_contacts);

    m_view = findViewById(R.id.lv_contactsSelect);
    m_view.setVisibility(View.VISIBLE);

    getListView().setItemsCanFocus(false);
    getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

    Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
                new String[]{ContactsContract.Contacts._ID,
                        ContactsContract.Contacts.DISPLAY_NAME},
                null, null, "UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ") ASC");
    setListAdapter(new SimpleCursorAdapter(this,
                android.R.layout.simple_list_item_multiple_choice,
                cursor,
                new String[]{ContactsContract.Contacts.DISPLAY_NAME},
                new int[]{android.R.id.text1},
                0));

    Button btn = findViewById(R.id.btn_get_contacts);
    btn.setOnClickListener((View view) -> {
        Intent replyIntent = new Intent();
        ArrayList<Long> ids = pickContacts();
        replyIntent.putExtra(Activities.ARG_SELECTED, ids);
        setResult(Activities.RESULT_CONTACTS_SELECTED, replyIntent);
        finish();
    });
}

/** Viewer for list of contacts */
private ListView m_view;

private ListView getListView ()
{ return m_view; }

private CursorAdapter mAdapter;

private void setListAdapter (@NonNull CursorAdapter adapter)
{
    mAdapter = adapter;
    m_view.setAdapter(adapter);
}

// return id for each selected contact
private ArrayList<Long> pickContacts ()
{
    SparseBooleanArray a = getListView().getCheckedItemPositions();
    ArrayList<Long> contacts = new ArrayList<>();
    for (int i = 0; i < a.size(); i++)
    {
        if (a.valueAt(i))
        {
            Cursor c = (Cursor)mAdapter.getItem(a.keyAt(i));
            // TODO use RawContacts or Contacts? Currently the result is the same.
            //Long idContact = c.getLong(c.getColumnIndex(ContactsContract.Contacts._ID));
            Long idRaw = c.getLong(c.getColumnIndex(ContactsContract.RawContacts._ID));
            contacts.add(idRaw);
        }
    }
    return contacts;
}

正在处理结果:

@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data)
{
    if (resultCode == Activities.RESULT_CONTACTS_SELECTED)
    {
        ArrayList<Long> ids = (ArrayList<Long>)data.getSerializableExtra(Activities.ARG_SELECTED);
        for (long id : ids)
        {
            getContactDetails(id);
        }
    }
}

/**
 * As mentioned in https://developer.android.com/reference/android/provider/ContactsContract.RawContacts
 * the best way to read a raw contact along with associated data is by using the Entity directory.
 */
// FIXME: The id from the received result matches what was selected,
// but this function reads details for a different contact.
private void getContactDetails (long rawContactId)
{
    System.out.println("Get contact with id " + rawContactId);
    Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
    Uri entityUri = Uri.withAppendedPath(rawContactUri, Entity.CONTENT_DIRECTORY);
    // For example, this output can be "Get contact from entity uri content://com.android.contacts/raw_contacts/615/entity"
    // (Where 615 is the id for the selected contact.)
    System.out.println("Get contact from entity uri " + entityUri);
    Cursor c = getContentResolver().query(entityUri,
            new String[]{RawContacts.SOURCE_ID, Entity.DATA_ID, Entity.MIMETYPE, Entity.DATA1}, // projection
            null, null, null);
    if (c == null)
        return;
    try
    {
        while (c.moveToNext())
        {
            // In this example I'm just dumping data to the console.
            if (!c.isNull(1))
            {
                String mimeType = c.getString(2);
                String data = c.getString(3);
                System.out.println("mimeType = " + mimeType);
                System.out.println("data = " + data);
            }
        }
    }
    finally
    {
        c.close();
    }
}

例如,处理程序的控制台输出包括:

mimeType = vnd.android.cursor.item/name
data = A name

联系人姓名与 selected 不同 selection activity。

在方法pickContacts中,改成Contacts._ID,偶然发现了一个ID,因为RawContacts._IDContacts._ID都是字符串"_id ",但这完全是错误的。

接下来,由于您实际上是在获取联系人 ID,因此您需要修改 getContactDetails 以接受 ContactId 而不是 RawContactId

不确定为什么需要涉及 Entity 这样的 API,如果您只需要查询该联系人的数据,请执行以下操作:

private void getContactDetails (long contactId) {
    Log.i("Contacts", "Get contact with id " + contactId);

    String[] projection = new String[]{Data.DISPLAY_NAME, Data.MIMETYPE, Data.DATA1};
    String selection = Data.CONTACT_ID + "=" + contactId;
    Cursor c = getContentResolver().query(Data.CONTENT_URI, projection, selection, null, null);
    if (c == null) {
        return;
    }
    try {
        while (c.moveToNext()) {
            String name = c.getString(0);
            String mimeType = c.getString(1);
            String data = c.getString(2);

            Log.i("Contacts", contactId + ", " + name + ", " + mimetype + ", " + data);
        }
    } finally {
        c.close();
    }
}