这几乎是与您的另一个问题具有相同答案的相同问题:When deleting a contact on android, other random contacts id's being changed
我正在尝试获取所有已更新的android联系人。
我正在将最新添加的联系人ID和最新更新的时间戳保存在Firebase上>>
我正在使用下一个函数来检索所有已更新联系人的游标,以便与Firebase服务器进行比较
private fun getUpdatedContacts(): Cursor? { val projection = arrayOf( ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.HAS_PHONE_NUMBER, ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP) val selection = ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " > ? AND " + ContactsContract.Contacts._ID + "<= ?" val selectionArgs = arrayOf(mFireContactDetails!!.lcu_ms.toString(), mFireContactDetails!!.lcid.toString()) val sortOrder = ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " ASC" return mContentResolver.query( ContactsContract.Contacts.CONTENT_URI, projection, selection, selectionArgs, sortOrder) }
但是当我在手机中更改一个联系人时,会返回许多我从未使用过的不相关联系人,并将它们标记为已更改。上一次,当我只是向现有联系人添加电话号码时,我从该游标中获取了50多个已更新的联系人。
[Android发生了什么?我现在尝试同步过去三个月的联系人。为什么这么难???
我正在尝试获取所有已更新的android联系人。我在Firebase上保存了我添加的最后一个联系人ID和上次更新的时间戳,我正在使用下一个函数来返回游标...
这几乎是与您的另一个问题具有相同答案的相同问题:When deleting a contact on android, other random contacts id's being changed
您对无法确定的联系人ID进行了一些假设-没有人保证联系人ID是递增的,也没有人保证联系人ID是稳定的,实际上绝对不是。
您可以在应用运行时使用查询的联系人ID,极少的机会在几分钟之内更改它们,但是有机会偶尔更改现有用户的ID。不仅如此,相同的ID还可以指向今天的某个联系人,而明天指向一个完全不同的联系人。
如果您将本地联系人的某些副本保留在云中,则应使用以下组合ID来引用联系人:Contacts.CONTACT_ID,Contacts.LOOKUP_KEY,Contacts.DISPLAY_NAME
在这里查看我的答案以获取更多详细信息:How to uniquely identify a contact on ContactsContract.Contacts table
这不是一个完美的解决方案,但这是我们拥有的最好的解决方案
我已经测试了此解决方案几天,似乎还可以,但是我认为我需要进行更多测试。如果您使用此方法,请进行自己的测试,最重要的是,如果我有任何遗漏,请让我知道,不要急于降级。谢谢!
withContext(Dispatchers.IO)
暂停任何代码以简化流程batchArray
,以避免超过500个限制现在,由于进行实际同步需要所有用户的访问权限,因此我使用.node作为用户的firebase功能。我创建2个函数:
两个功能都将用户与文档中的规范化数字进行比较,如果匹配,则将该用户的uid写入firestore文档的“ friend_uid”字段。 [请注意,如果您尝试在免费的Firebase计划中使用这些功能,可能会出错。我建议更改为Blaze计划,并将收费限制在几美元。通过更改为Blaze,Google还会为您提供免费的附加功能,并避免实际付款 到此,同步完成。同步仅需几秒钟
要显示该应用程序用户的所有联系人,用“ friend_uid”查询所有不为空的用户联系人。
一些额外的注释:
.get(Source.DEFAULT)
,其他时候使用.get(Source.CACHE)
。由于这些文档的名称和编号仅由用户修改,因此我相信大多数情况下这不是问题(仍在测试中)某些源代码:
private fun getContacts(): Cursor? {
val projection = arrayOf(
ContactsContract.CommonDataKinds.Phone._ID,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.CONTACT_LAST_UPDATED_TIMESTAMP)
//sort by NORMALIZED_NUMBER to detect duplicates and then by name to keep order and avoiding name change
val sortOrder = ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER + " ASC, " +
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC"
return mContentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection,
null,
null,
sortOrder)
}
private suspend fun syncContactsAsync() = withContext(Dispatchers.IO) {
if (isAnythingChanged() || mFirstRun) {
if (getValues() == Result.SUCCESS) {
myPrintln("values retrieved success")
} else {
myPrintln("values retrieved failed. Aborting.")
return@withContext
}
val cursor: Cursor? = getContacts()
if (cursor == null) {
myPrintln("cursor cannot be null")
mFireContactHashMap.clear()
return@withContext
}
if (cursor.count == 0) {
cursor.close()
mFireContactHashMap.clear()
myPrintln("cursor empty")
return@withContext
}
var contactName: String?
var internalContact: InternalContact?
val batchArray = mutableListOf(FirebaseFirestore.getInstance().batch())
var batchIndex = 0
var batchCount = 0
var normalizedNumber:String?
var prevNumber = ""
var firestoreId: String
while (cursor.moveToNext()) {
normalizedNumber = cursor.getString(COLUMN_UPDATED_NORMALIZED_NUMBER)
if (normalizedNumber == null) {
normalizedNumber = cursor.getString(COLUMN_UPDATED_PHONE_NUMBER)
normalizedNumber = Phone.getParsedPhoneNumber(mDeviceCountryIso,normalizedNumber,mContext)
}
//cursor sorted by normalized numbers so if same as previous, do not check
if (normalizedNumber != prevNumber) {
prevNumber = normalizedNumber
contactName = cursor.getString(COLUMN_UPDATED_DISPLAY_NAME)
internalContact = mFireContactHashMap[normalizedNumber]
//if phone number exists on firestore
if (internalContact != null) {
//if name changed, update in firestore
if (internalContact.name != contactName) {
myPrintln("updating $normalizedNumber from name: ${internalContact.name} to: $contactName")
batchArray[batchIndex].update(
mFireContactRef.document(internalContact.id),
FireContact.COLUMN_NAME,
contactName)
batchCount++
}
//remove to avoid deletions
mFireContactHashMap.remove(normalizedNumber)
} else {
//New item. Insert
if (normalizedNumber != mUserPhoneNumber) {
myPrintln("adding $normalizedNumber / $contactName")
firestoreId = mFireContactRef.document().id
batchArray[batchIndex].set(mFireContactRef.document(firestoreId),
FireContact(firestoreId, -1, contactName,
cursor.getString(COLUMN_UPDATED_PHONE_NUMBER),
normalizedNumber))
batchCount++
}
}
if (BATCH_HALF_MAX < batchCount ) {
batchArray += FirebaseFirestore.getInstance().batch()
batchCount = 0
batchIndex++
}
}
}
cursor.close()
//Remaining contacts not found on cursor so assumed deleted. Delete from firestore
mFireContactHashMap.forEach { (key, value) ->
myPrintln("deleting ${value.name} / $key")
batchArray[batchIndex].delete(mFireContactRef.document(value.id))
batchCount++
if (BATCH_HALF_MAX < batchCount ) {
batchArray += FirebaseFirestore.getInstance().batch()
batchCount = 0
batchIndex++
}
}
//execute all batches
if ((batchCount > 0) || (batchIndex > 0)) {
myPrintln("committing changes...")
batchArray.forEach { batch ->
batch.commit()
}
} else {
myPrintln("no records to commit")
}
myPrintln("end sync")
mFireContactHashMap.clear()
mPreferenceManager.edit().putLong(PREF_LAST_TIMESTAMP,mLastContactUpdated).apply()
mFirstRun = false
} else {
myPrintln("no change in contacts")
}
}
private suspend fun putAllUserContactsToHashMap() : Result {
var result = Result.FAILED
val batchArray = mutableListOf(FirebaseFirestore.getInstance().batch())
var batchIndex = 0
var batchCount = 0
mFireContactHashMap.clear()
var source = Source.CACHE
if (mFirstRun) {
source = Source.DEFAULT
myPrintln("get contacts via Source.DEFAULT")
} else {
myPrintln("get contacts via Source.CACHE")
}
mFireContactRef.whereEqualTo( FireContact.COLUMN_USER_ID,mUid ).get(source)
.addOnSuccessListener {documents ->
var fireContact : FireContact
for (doc in documents) {
fireContact = doc.toObject(FireContact::class.java)
if (!mFireContactHashMap.containsKey(fireContact.paPho)) {
mFireContactHashMap[fireContact.paPho] = InternalContact(fireContact.na, doc.id)
} else {
myPrintln("duplicate will be removed from firestore: ${fireContact.paPho} / ${fireContact.na} / ${doc.id}")
batchArray[batchIndex].delete(mFireContactRef.document(doc.id))
batchCount++
if (BATCH_HALF_MAX < batchCount) {
batchArray += FirebaseFirestore.getInstance().batch()
batchCount = 0
batchIndex++
}
}
}
result = Result.SUCCESS
}.addOnFailureListener { exception ->
myPrintln("Error getting documents: $exception")
}.await()
//execute all batches
if ((batchCount > 0) || (batchIndex > 0)) {
myPrintln("committing duplicate delete... ")
batchArray.forEach { batch ->
batch.commit()
}
} else {
myPrintln("no duplicates to delete")
}
return result
}
这几乎是与您的另一个问题具有相同答案的相同问题:When deleting a contact on android, other random contacts id's being changed
我已经测试了此解决方案几天,似乎还可以,但是我认为我需要进行更多测试。如果您使用此方法,请进行自己的测试,最重要的是,如果我有任何遗漏,请让我知道,不要急于降级。谢谢!