赞
踩
添加权限:<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
,6.0以上需要动态申请。如果是小米手机的无法申请,需要手动到设置中允许权限。
注册"android.intent.action.SIM_STATE_CHANGED"
广播
registerReceiver(receiver, IntentFilter("android.intent.action.SIM_STATE_CHANGED"))
获取intent中的ss字段
class SimCardReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
intent.extras?.keySet()?.forEach { key -> Timber.i("$key = ${intent.extras?.get(key)}") }
val ss = intent.getStringExtra("ss")
when (ss) {
"LOADED" -> Timber.i("有sim卡")
"ABSENT" -> Timber.i("没有sim卡")
}
}
}
在我的小米手机上,插入的双卡,卡1电信,卡2联通,运行结果如下:
android.telephony.extra.SUBSCRIPTION_INDEX = 1
slot_id = 1
subscription_id = 1
phone_id = 1
android.telephony.extra.SLOT_INDEX = 1
phoneName = Phone
reason = null
rebroadcastOnUnlock = true
ss = LOADED
phone = 1
subscription = 1
有sim卡
这是一个粘性广播,一注册就会立马收到广播,返回的结果中SLOT_INDEX为1,索引是从0开始的,说明这是返回的卡2的信息。
此时我将卡拨出,两个卡都会被同时拨出,结果如下:
slot_id = 0 subscription_id = -1 phone_id = 0 android.telephony.extra.SLOT_INDEX = 0 phoneName = Phone reason = null ss = ABSENT phone = 0 没有sim卡 slot_id = 1 subscription_id = -1 phone_id = 1 android.telephony.extra.SLOT_INDEX = 1 phoneName = Phone reason = null ss = ABSENT phone = 1 没有sim卡
这里可以看到卡1卡2同时被拨出来了。
我再把两张卡同时插回去:
android.telephony.extra.SUBSCRIPTION_INDEX = 1 slot_id = 1 subscription_id = 1 phone_id = 1 android.telephony.extra.SLOT_INDEX = 1 phoneName = Phone reason = null ss = NOT_READY phone = 1 subscription = 1 android.telephony.extra.SUBSCRIPTION_INDEX = 2 slot_id = 0 subscription_id = 2 phone_id = 0 android.telephony.extra.SLOT_INDEX = 0 phoneName = Phone reason = null ss = NOT_READY phone = 0 subscription = 2 android.telephony.extra.SUBSCRIPTION_INDEX = 2 slot_id = 0 subscription_id = 2 phone_id = 0 android.telephony.extra.SLOT_INDEX = 0 phoneName = Phone reason = null ss = READY phone = 0 subscription = 2 android.telephony.extra.SUBSCRIPTION_INDEX = 1 slot_id = 1 subscription_id = 1 phone_id = 1 android.telephony.extra.SLOT_INDEX = 1 phoneName = Phone reason = null ss = READY phone = 1 subscription = 1 android.telephony.extra.SUBSCRIPTION_INDEX = 2 slot_id = 0 subscription_id = 2 phone_id = 0 android.telephony.extra.SLOT_INDEX = 0 phoneName = Phone reason = null ss = LOADED phone = 0 subscription = 2 有sim卡 android.telephony.extra.SUBSCRIPTION_INDEX = 1 slot_id = 1 subscription_id = 1 phone_id = 1 android.telephony.extra.SLOT_INDEX = 1 phoneName = Phone reason = null ss = LOADED phone = 1 subscription = 1 有sim卡
这里收到了6条广播,每张卡3条广播,每张卡的ss分别经历3个状态,如下:
NOT_READY
READY
LOADED
当把卡拨掉的时候它返回的是ABSENT
,所以可以认为如果是ABSENT
就是无卡,其它状态就是有卡。
两张卡已经插入的情况下,此时再注册广播只会收到卡2的一条广播,ss为LOADED
,为什么会这样我也搞不清楚。所以,这种方式适合判断单卡的设备是否插入了sim卡,如果是双卡设备,你就无法判断是否插入了双卡,因为当你注册的时候它只接收一条卡的广播信息,此时你拨卡再插卡的时候才会收到两张卡的广播信息。 所以它只适合判断单卡,只要你一注册你就能获得那个卡槽的信息,无论是否当前有卡还是无卡。
在双卡的情况下,插入了两张卡,拨卡的时候能收到两张卡的广播信息,但是,当我注册的时候,如果双卡之前就已经拨掉了,则此时我只收到一条广播,如下:
slot_id = 1
subscription_id = -1
phone_id = 1
android.telephony.extra.SLOT_INDEX = 1
phoneName = Phone
reason = null
ss = ABSENT
phone = 1
没有sim卡
可以看到,收到的是卡2的信息。
"android.intent.action.SIM_STATE_CHANGED"
实际上为Intent.ACTION_SIM_STATE_CHANGED
,但它是系统API,我们无法直接调用,所以只能直接使用它的值。查看该常量已经过期,推荐使用TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED
或TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED
,这两也是系统API,所以只能使用它们的值来注册。
首先看TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED
的源码,它的值为"android.telephony.action.SIM_CARD_STATE_CHANGED"
,收到广播后,它通过TelephonyManager.EXTRA_SIM_STATE
来获取状态,状态包括:
Requires the READ_PRIVILEGED_PHONE_STATE permission. The current state can also be queried using getSimCardState(). This is a protected intent that can only be sent by the system.
通过阅读API文档可知,这要求系统App才能收到这个广播,因为只有系统APP才能申请READ_PRIVILEGED_PHONE_STATE
权限。且这个SIM卡的状态也可通过TelephonyManager.getSimCardState()
来获取。
对于TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED
,它获取的状态有:
该广播同样需要系统级App才能接收。该状态可通过TelephonyManager.getSimApplicationState()
函数获取。
通过查看源码,发现TelephonyManager
中还有一个ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED"
广播,对应状态可使用getUiccSlotsInfo()
函数获取。
经过试验,我发现即使我的应用有系统签名,也无法接收TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED
和TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED
这两个广播。运行的是在Android7.1.1版本,不知道是否是系统版本的问题,有时间再Android11中再试一下。
现在能肯定的是第一种方式是OK,即便在我的小米11pro(Android13)中依旧可以监听到sim卡状态的变化,完整示例代码如下:
class SimCardReceiver : BroadcastReceiver() { private lateinit var callback: (Boolean) -> Unit override fun onReceive(context: Context, intent: Intent) { val noCard = intent.getStringExtra("ss") == "ABSENT" callback(noCard) } companion object { private const val ACTION_SIM_STATE_CHANGED: String = "android.intent.action.SIM_STATE_CHANGED" // 等于:Intent.ACTION_SIM_STATE_CHANGED private var isRegister = false private val receiver: SimCardReceiver by lazy { SimCardReceiver() } fun register(context: Context, action: String,callback: (Boolean) -> Unit) { receiver.callback = callback unregister(context) context.registerReceiver(receiver, IntentFilter(ACTION_SIM_STATE_CHANGED)) isRegister = true } fun unregister(context: Context) { if (isRegister) { context.unregisterReceiver(receiver) isRegister = false } } } }
调用:
SimCardReceiver.register(this) { noCard ->
Timber.i(if (noCard) "无卡" else "有卡")
}
SimCardReceiver.unregister(this)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。