Kotlin无需READ_SMS权限实现短信OTP自动填充的方案问询
Android Kotlin 短信OTP自动填充实现方案
核心问题解答
- 谷歌SMS Retriever API不是实现该功能的唯一方式
- 存在完全不依赖第三方库的原生实现方案
- 无需修改现有短信模板的可行方案共有两种,可根据你的上架场景选择
方案1:原生广播监听实现(无第三方依赖、无需修改短信模板、国内上架推荐)
该方案仅依赖安卓系统原生API,不需要引入任何外部库,也不需要对现有短信模板做任何调整,可实现全自动OTP提取填充,适配所有安卓版本。
注意事项
- 需要向用户申请
READ_SMS、RECEIVE_SMS权限,用户授权后才可正常使用 - 若上架谷歌Play商店,需要向谷歌说明你的应用核心功能依赖短信读取权限,否则可能会被驳回;国内各应用商店无此限制
- 安卓10及以上版本不支持静态注册短信广播,必须在页面活跃时动态注册广播接收器
实现步骤
- 先在
AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" />
- 页面初始化时动态申请短信权限:
private val SMS_PERMISSION_CODE = 1001 private fun requestSmsPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.READ_SMS, Manifest.permission.RECEIVE_SMS), SMS_PERMISSION_CODE ) } }
- 动态注册短信广播接收器,匹配提取OTP:
private lateinit var smsReceiver: BroadcastReceiver private fun registerSmsReceiver() { smsReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action != Telephony.Sms.Intents.SMS_RECEIVED_ACTION) return val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent) messages.forEach { sms -> val smsContent = sms.messageBody // 按你的OTP规则匹配,示例为匹配6位连续数字 val otpRegex = Regex("\\d{6}") otpRegex.find(smsContent)?.let { match -> // 直接填充到输入框 etOtpInput.setText(match.value) // 匹配成功后注销广播避免重复触发 unregisterReceiver(this) } } } } val filter = IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION) registerReceiver(smsReceiver, filter) } // 页面销毁时记得注销广播 override fun onDestroy() { super.onDestroy() if (::smsReceiver.isInitialized) unregisterReceiver(smsReceiver) }
方案2:SMS User Consent API(无需修改短信模板、无需申请短信权限、海外上架推荐)
该方案为谷歌官方提供,不需要修改现有短信模板,也不需要申请短信读取权限,仅需用户单次确认授权即可获取短信内容提取OTP,适配所有搭载谷歌Play服务的设备。
注意事项
- 仅支持搭载谷歌Play服务的设备,国内行货设备默认无该服务无法使用
- 收到短信后系统会弹出授权弹窗,用户点击同意后才可获取短信内容,无法实现完全无感填充
核心实现代码
- 初始化监听:
private val SMS_CONSENT_CODE = 1002 private fun startSmsConsentListen() { SmsRetriever.getClient(this).startSmsUserConsent(null) val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION) registerReceiver(smsConsentReceiver, intentFilter, SmsRetriever.SEND_PERMISSION, null) }
- 处理授权结果:
private val smsConsentReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action != SmsRetriever.SMS_RETRIEVED_ACTION) return val extras = intent.extras ?: return val status = extras.get(SmsRetriever.EXTRA_STATUS) as Status when (status.statusCode) { CommonStatusCodes.SUCCESS -> { val consentIntent = extras.getParcelable<Intent>(SmsRetriever.EXTRA_CONSENT_INTENT) consentIntent?.let { startActivityForResult(it, SMS_CONSENT_CODE) } } } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == SMS_CONSENT_CODE && resultCode == RESULT_OK) { val smsContent = data?.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE) smsContent?.let { // 提取OTP填充到输入框即可 val otp = Regex("\\d{6}").find(it)?.value ?: return etOtpInput.setText(otp) } } }
内容的提问来源于stack exchange,提问作者LMaker
相关产品推荐
相关产品推荐

