Android双卡设备指定SIM卡存储联系人的实现方案咨询
解决方案:指定SIM卡存储联系人
我之前也碰到过这个问题,Android上指定SIM卡存储联系人确实因为厂商差异有点麻烦,不过可以通过以下方案解决:
核心原理
Android系统中,每张SIM卡的联系人存储URI是独立的,并非统一的content://icc/adn。我们需要先获取目标SIM卡的订阅信息(比如subscriptionId或iccId),再构建对应SIM卡的专属URI,最后用这个URI插入联系人。
步骤1:添加必要权限
首先在AndroidManifest.xml中声明权限:
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" />
注意:Android 6.0及以上版本需要动态申请这两个权限,否则无法获取SIM卡信息和写入联系人。
步骤2:获取SIM卡订阅信息
通过TelephonyManager获取设备上的活跃SIM卡列表,这样可以让用户选择要存储的SIM卡(比如根据卡槽ID、运营商名称区分):
private fun getActiveSimList(): List<SubscriptionInfo>? { val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { // Android 5.1及以上支持获取订阅信息 telephonyManager.activeSubscriptionInfoList } else { // 低于5.1的版本兼容性较差,建议提示用户无法指定SIM卡 null } }
你可以把这个列表展示给用户,让他们选择SIM1或SIM2,记录对应的slotIndex(卡槽ID,通常0是SIM1,1是SIM2)。
步骤3:构建目标SIM卡的联系人URI
不同厂商的SIM卡URI格式可能不同,我们需要尝试几种常见格式,找到可写入的URI:
private fun getTargetSimUri(selectedSlotId: Int): Uri? { val simList = getActiveSimList() ?: return null val targetSim = simList.find { it.slotIndex == selectedSlotId } ?: return null // 常见的SIM卡联系人URI格式,按优先级尝试 val possibleUris = listOf( "content://icc/adn/subId/${targetSim.subscriptionId}", "content://icc/adn/${targetSim.iccId}", "content://sim/adn/subId/${targetSim.subscriptionId}", "content://sim/adn/${targetSim.iccId}" ) // 验证URI是否可写入(插入测试数据后立即删除) for (uriStr in possibleUris) { val uri = Uri.parse(uriStr) try { val testUri = context.contentResolver.insert(uri, ContentValues().apply { put("tag", "temp_test") put("number", "12345") }) testUri?.let { context.contentResolver.delete(it, null, null) return uri } } catch (e: Exception) { // 当前URI不可用,继续尝试下一个 } } return null }
步骤4:修改存储函数,支持指定SIM卡
更新你原来的函数,传入用户选择的卡槽ID,使用对应的URI存储联系人:
private fun addContactToSim(number: String, name: String, selectedSlotId: Int) { try { val simUri = getTargetSimUri(selectedSlotId) ?: run { Log.e("SimContact", "无法找到目标SIM卡的有效存储URI") return } val cv = ContentValues().apply { put("tag", name) // SIM卡联系人的姓名字段 put("number", number) // SIM卡联系人的号码字段 // 注意:SIM卡通常不支持邮箱字段,插入后可能被忽略,建议移除 // put("email", "Email") } context.contentResolver.insert(simUri, cv)?.let { context.contentResolver.notifyChange(simUri, null) Log.d("SimContact", "联系人已成功存入指定SIM卡") } ?: Log.e("SimContact", "插入联系人失败") } catch (e: Exception) { e.printStackTrace() Log.e("SimContact", "存储联系人出错:${e.message}") } }
注意事项
- 字段限制:绝大多数SIM卡仅支持姓名(
tag)和号码(number)字段,邮箱、地址等字段无法存储,插入后会被忽略。 - 兼容性:部分厂商可能限制第三方应用写入SIM卡,此时即使代码正确也无法完成操作,需要提示用户。
- 旧版本兼容:Android 5.1以下版本没有统一的SIM卡订阅API,很难实现指定SIM卡存储,建议针对这些版本做降级处理(比如默认存储到默认SIM卡)。
内容的提问来源于stack exchange,提问作者Manoj Kumar




