部分Android设备中添加联系人Intent未在onActivityResult返回数据
你遇到的是厂商定制ROM的行为差异——索尼Xperia的系统联系人应用在完成联系人插入后,没有遵循标准Android规范返回包含新联系人Uri的Intent data,虽然返回了RESULT_OK(即-1),但data为null。这是定制ROM里很常见的兼容性问题,下面给你几个可行的解决思路:
1. 调整Intent的构造方式
你当前的Intent同时设置了ACTION_INSERT和setType(ContactsContract.Contacts.CONTENT_TYPE),部分厂商的应用对这种组合的处理不符合标准。可以尝试简化Intent的构造逻辑:
private void addContactUsingIntent() { Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION); intent.setType(ContactsContract.RawContacts.CONTENT_TYPE); // 如果需要提前填充联系人信息,可以在这里添加extra参数 // intent.putExtra(ContactsContract.Intents.Insert.NAME, "预设姓名"); // intent.putExtra(ContactsContract.Intents.Insert.PHONE, "预设电话"); intent.putExtra(Intent.EXTRA_FINISH_ON_COMPLETION, true); startActivityForResult(intent, INSERT_CONTACT_REQUEST); }
使用ContactsContract.Intents.Insert.ACTION是官方推荐的插入联系人的Intent构造方式,兼容性会更好。
2. 当data为null时,查询最新添加的联系人
既然resultCode == RESULT_OK,说明联系人已经成功保存,我们可以直接通过Contacts Provider查询最近创建的联系人来获取数据:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == INSERT_CONTACT_REQUEST) { if (resultCode == RESULT_OK) { Uri contactData = data.getData(); long newId = -1; String name = ""; // 处理data为null的情况 if (contactData == null) { // 按创建时间倒序查询,取最新的一条联系人 String[] projection = { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }; String sortOrder = ContactsContract.Contacts.CREATED_TIMESTAMP + " DESC LIMIT 1"; Cursor cursor = getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, projection, null, null, sortOrder ); if (cursor != null && cursor.moveToFirst()) { newId = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID)); name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME)); cursor.close(); } } else { // 原有处理data不为null的逻辑 Cursor cursor = getContentResolver().query(contactData, null, null, null, null); if (cursor.moveToFirst()) { newId = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID)); name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME)); cursor.close(); } } // 如果成功获取到联系人ID,继续获取电话、邮箱等信息 if (newId != -1) { String email = getEmail(String.valueOf(newId)); ArrayList<String> phones = new ArrayList<>(); Cursor cPhones = getContentResolver().query( ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[]{String.valueOf(newId)}, null ); while (cPhones.moveToNext()) { phones.add(cPhones.getString(cPhones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))); } cPhones.close(); Uri photoUri = getPhotoUri(newId); Log.i("Photo URI", photoUri.toString()); updateViews(newId, name, phones, email, photoUri); } } } }
注意:这个方法依赖CREATED_TIMESTAMP字段,Android 3.0(API 11)及以上支持,你的设备都是Android 6.0,完全没问题。另外要确保已经申请了READ_CONTACTS的动态权限(Android 6.0及以上需要)。
3. 直接使用Contacts Provider插入联系人(最可控)
如果依赖系统Intent的兼容性问题始终无法解决,可以跳过系统联系人应用,直接通过ContentResolver插入联系人,这样完全不受厂商ROM的影响:
private void addContactDirectly(String name, String phone, String email) { // 1. 插入RawContact ContentValues values = new ContentValues(); Uri rawContactUri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values); long rawContactId = ContentUris.parseId(rawContactUri); // 2. 插入姓名 values.clear(); values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); values.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name); getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values); // 3. 插入电话(如果有) if (!TextUtils.isEmpty(phone)) { values.clear(); values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone); values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE); getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values); } // 4. 插入邮箱(如果有) if (!TextUtils.isEmpty(email)) { values.clear(); values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId); values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE); values.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email); values.put(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK); getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values); } // 5. 获取对应的Contact ID并更新界面 Cursor cursor = getContentResolver().query( ContactsContract.RawContacts.CONTENT_URI, new String[]{ContactsContract.RawContacts.CONTACT_ID}, ContactsContract.RawContacts._ID + " = ?", new String[]{String.valueOf(rawContactId)}, null ); if (cursor != null && cursor.moveToFirst()) { long contactId = cursor.getLong(0); cursor.close(); ArrayList<String> phonesList = new ArrayList<>(); phonesList.add(phone); Uri photoUri = getPhotoUri(contactId); updateViews(contactId, name, phonesList, email, photoUri); } }
这种方法需要申请WRITE_CONTACTS动态权限,但好处是完全可控,不会受到任何厂商ROM的限制。
额外注意事项
- 务必在AndroidManifest.xml中声明必要权限:
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" />
- Android 6.0及以上需要动态申请这些权限,在调用相关代码前一定要检查权限是否已授予。
内容的提问来源于stack exchange,提问作者Travolta John

