这是如何正确地将照片设置为地址簿上的联系人?

时间:2017-08-26 11:45:39

标签: android bitmap android-contacts

背景

假设我通过在地址簿上使用某些查询找到了联系人。通过执行查询来找到来自特定帐户(例如WhatsApp)的联系信息,正如我写的那样here

现在我有一张图片,我希望用它来更新联系人的照片。

问题

我根据我在StackOverflow上找到的更新联系人照片的内容创建了代码。

事实上,有些用户声称它没有做任何事情。我不确定是什么原因造成的。也许访问联系人的方式不好(id / lookup-key?)。也许我需要一个额外的地址簿字段。也许查询本身是错误的......

我在弄清楚这个问题的原因时遇到了问题,因为我无法重现它,大多数用户也是如此。

代码

这就是我的所作所为:

        final ArrayList<ContentProviderOperation> ops = new ArrayList<>();
        final String lookupKey=...;
        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            final byte[] photoByteArray = new byte[(int) file.length()];
            fileInputStream.read(photoByteArray);
            fileInputStream.close();
            final Builder builder = ContentProviderOperation.newUpdate(Data.CONTENT_URI);
            builder.withSelection(Data.LOOKUP_KEY + "=?" + " AND " + Data.MIMETYPE + "=?", new String[]{lookupKey, Photo.CONTENT_ITEM_TYPE});
            builder.withValue(Photo.PHOTO, photoByteArray);
            ops.add(builder.build());
            file.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
        context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops )

问题

代码有什么问题吗?

我应该以不同方式更新联系人吗?

为什么有些用户无法更新?查找键不够吗?它可以更新错误的数据吗?

我已经读过某些地方,我可能需要使用RAW-contact-id,但无法找到准确的操作。查询然后更新/插入?

有人建议开发人员应考虑使用其应用帐户来保存照片,而不是更新,因为它可能会导致问题。这是真的吗?

1 个答案:

答案 0 :(得分:2)

我在代码中看到了许多可能的问题:

  1. 您假设已为该联系人存储了现有照片(您正在使用newUpdate),如果目前没有照片,则应该newInsert代替。
  2. 您应该更新/插入特定的RawContact,您所写的内容更新所有Contact的RawContacts照片数据原始数据,一些RawContacts可能具有只读状态,并且不允许您的应用修改它。
  3. 处理照片,尤其是将照片存储到“联系人”数据库​​时,这是一项内存非常紧张的操作,对于某些设备,它可能会在OutOfMemoryError上崩溃,因为它们的内存非常有限,或者因为内存目前被大量使用别的。您应该使用try / catch包装整个代码,包括applyBatch,并捕获所有异常/错误并将其报告给日志,最好也报告给您的错误报告库(Firebase / crashlytics / etc)
  4. 您只是更新数据原始的PHOTO字段,您还应该更新PHOTO_FILE_ID字段,因为某些应用可能会选择阅读PHOTO_FILE_ID并从中获取照片代替。
  5. 您没有将新照片设置为其联系人的SUPER_PRIMARY,因此如果同一个联系人中的其他一些RawContact也有照片,则可能会“赢”,并成为该联系人的主要照片
  6. 以下代码应该有所帮助:

    // To simplify the code i didn't worry about try/catch, freeing resources, 
    // or running slow queries on the main thread, make sure your code does!
    public ContentProviderOperation setPhoto(int contactId, byte[] photoByteArray) {
        long rawId = getRawId(contactId);
    
        ContentProviderOperation.Builder builder;
    
        String selection = Data.RAW_CONTACT_ID + " = '" + rawId + "' AND " + Data.MIMETYPE + "= '" + Photo.CONTENT_ITEM_TYPE + "'";
        Cursor cur = contentResolver.query(Data.CONTENT_URI, new String[]{ Data._ID }, selection, null, null);
        if (cur.moveToFirst()) {
            // this RawContact already has a photo!
            Long photoDataId = cur.getLong(0);
            Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, photoDataId);
            builder = ContentProviderOperation.newUpdate(uri);
        } else {
            // this RawContact has no photo.
            builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
            builder.withValue(Data.RAW_CONTACT_ID, rawId);
        }
        builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
        builder.withValue(Data.IS_SUPER_PRIMARY, 1);
        builder.withValue(Data.IS_PRIMARY, 1);
    
        builder.withValue(Photo.PHOTO, photoByteArray);
        builder.withValue(Photo.PHOTO_FILE_ID, null);
    
        return builder.build();
    }
    
    private long getRawId(long contactId) {
        String selection = RawContacts.CONTACT_ID + "='" + contactId + "'";
        Cursor cur = contentResolver.query(RawContacts.CONTENT_URI, new String[]{ RawContacts._ID }, selection, null, null);
        try {
            if (cur.moveToNext()) {
                return cur.getLong(0);
            }
        } finally {
            cur.close();
        }
        return 0;
    }