Android:如何通过代码创建并保存联系人至SIM卡?代码存卡异常咨询
Hey there! Let's dive into your two Android SIM card contact questions—they're super common when dealing with SIM storage, so I've got you covered.
Saving contacts to a SIM card relies on Android's ContentProvider for SIM storage, but there are a few critical steps to get right:
- First, secure necessary permissions: You’ll need
WRITE_CONTACTSandREAD_CONTACTSpermissions. For Android 6.0 (API 23) and above, you have to request these dynamically at runtime, not just declare them in the manifest. - Use the correct SIM content URI: The standard URI is
content://icc/adn, but some manufacturers use variations (likecontent://sim/adnfor certain devices). For dual-SIM phones, target a specific SIM by appending the subscription ID, e.g.,content://icc/adn/subId/{subId}. - Build and insert contact data: Create a
ContentValuesobject with the contact’s name (stored under thetagkey) and phone number (undernumber), then useContentResolver.insert()to add it to the SIM. - Handle exceptions: SIM operations can fail for various reasons (full storage, unsupported SIM, permission issues), so always wrap code in a try-catch block.
Here’s a basic implementation outline:
private const val PERMISSION_REQUEST_CODE = 1001 private fun requestPermissionsIfNeeded(context: Context) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(context as Activity, arrayOf(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS), PERMISSION_REQUEST_CODE) } } private fun addContactToSim(context: Context, number: String, name: String) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) { Log.e("SimContact", "WRITE_CONTACTS permission not granted") return } try { val simUri = Uri.parse("content://icc/adn") val cv = ContentValues().apply { put("tag", name) put("number", number) } val insertedUri = context.contentResolver.insert(simUri, cv) if (insertedUri != null) { // Notify system of the change context.contentResolver.notifyChange(simUri, null) // Trigger a refresh for the contacts app val refreshIntent = Intent(Intent.ACTION_PROVIDER_CHANGED).apply { data = ContactsContract.Contacts.CONTENT_URI } context.sendBroadcast(refreshIntent) Log.d("SimContact", "Contact added successfully") } else { Log.e("SimContact", "Failed to insert contact to SIM") } } catch (e: Exception) { e.printStackTrace() Log.e("SimContact", "Error adding contact: ${e.message}") } }
Your existing code is a solid starting point, but the inconsistent display usually stems from system caching, URI mismatches, or missing refresh triggers. Let’s break down the fixes:
Common Causes & Solutions:
Incorrect SIM URI: Some devices use non-standard URIs, and dual-SIM phones might require targeting a specific SIM. To fix this:
- Use
TelephonyManagerto get active SIM subscription IDs and build the correct URI:val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager val subscriptionInfos = telephonyManager.activeSubscriptionInfoList subscriptionInfos?.forEach { info -> val simUri = Uri.parse("content://icc/adn/subId/${info.subscriptionId}") // Try inserting to this SIM-specific URI } - Test
content://sim/adnif the standard URI doesn’t work for your device.
- Use
System contact app caching: The default contacts app might not immediately pick up new SIM contacts. Your
notifyChangecall helps, but adding a broadcast ensures the app refreshes:val refreshIntent = Intent(Intent.ACTION_PROVIDER_CHANGED).apply { data = ContactsContract.Contacts.CONTENT_URI addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) } context.sendBroadcast(refreshIntent)Unverified insertion success: No exception doesn’t mean the insert worked. Always check if
insert()returns a non-null URI. If it’s null, the operation failed (maybe the SIM is full or unsupported).SIM storage limits: SIM cards have fixed contact slots (usually 200-500). Check available space first:
val cursor = context.contentResolver.query(simUri, null, null, null, null) val contactCount = cursor?.count ?: 0 cursor?.close() if (contactCount >= 500) { // Adjust based on typical SIM limits Log.e("SimContact", "SIM is full") return }Manufacturer-specific quirks: OEMs like Samsung or Xiaomi have custom contact sync logic. For these devices, add a short delay after insertion or trigger a manual sync (requires additional permissions).
Optimized Version of Your Code:
Here’s how to update your function with these fixes:
private fun addContactToSim(context: Context, number: String, name: String) { if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) { Log.e("SimContact", "WRITE_CONTACTS permission not granted") return } val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager val subscriptionInfos = telephonyManager.activeSubscriptionInfoList // Try inserting to each available SIM subscriptionInfos?.forEach { info -> try { val simUri = Uri.parse("content://icc/adn/subId/${info.subscriptionId}") // Check SIM storage first val cursor = context.contentResolver.query(simUri, null, null, null, null) val contactCount = cursor?.count ?: 0 cursor?.close() if (contactCount >= 500) { Log.e("SimContact", "SIM ${info.displayName} is full") return@forEach } val cv = ContentValues().apply { put("tag", name) put("number", number) } val insertedUri = context.contentResolver.insert(simUri, cv) if (insertedUri != null) { context.contentResolver.notifyChange(simUri, null) // Broadcast to refresh contacts app val refreshIntent = Intent(Intent.ACTION_PROVIDER_CHANGED).apply { data = ContactsContract.Contacts.CONTENT_URI addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) } context.sendBroadcast(refreshIntent) Log.d("SimContact", "Contact added to SIM ${info.displayName}") } else { Log.e("SimContact", "Failed to insert to SIM ${info.displayName}") } } catch (e: Exception) { e.printStackTrace() Log.e("SimContact", "Error inserting to SIM ${info.displayName}: ${e.message}") } } // Fallback to default URI if no subscription info found if (subscriptionInfos.isNullOrEmpty()) { try { val simUri = Uri.parse("content://icc/adn") val cv = ContentValues().apply { put("tag", name) put("number", number) } val insertedUri = context.contentResolver.insert(simUri, cv) if (insertedUri != null) { context.contentResolver.notifyChange(simUri, null) val refreshIntent = Intent(Intent.ACTION_PROVIDER_CHANGED).apply { data = ContactsContract.Contacts.CONTENT_URI } context.sendBroadcast(refreshIntent) } } catch (e: Exception) { e.printStackTrace() } } }
内容的提问来源于stack exchange,提问作者MARSH

