You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android Kotlin监听设备新短信事件功能失效问题排查及修复咨询

我帮你排查了代码里的几个关键问题,这些都是导致短信通知不工作的核心原因,咱们一步步来修复:

一、广播注册的致命错误

1. 静态注册的系统限制

Android 8.0(API 26)之后,系统严格限制了隐式广播的静态注册android.provider.Telephony.SMS_RECEIVED就属于这类广播。你在AndroidManifest.xml里的静态注册,在高版本系统上根本不会被触发。

2. 动态注册的Action错误

你在MainActivity里注册广播时,监听的是自定义的com.forkthecoup.com22222.SmsListener Action,而不是短信系统广播的android.provider.Telephony.SMS_RECEIVED,这直接导致接收器收不到短信触发的广播信号。

修复方案
移除Manifest里的静态广播注册(保留也不会生效,建议直接删除),在MainActivity里正确注册短信广播,并且记得在Activity销毁时注销接收器避免内存泄漏。

二、权限问题(容易遗漏的动态申请)

虽然你说已经授予权限,但Android 6.0(API 23)+ 需要动态申请危险权限

  • RECEIVE_SMS:必须动态申请才能接收短信
  • Android 13(API 33)+ 还需要POST_NOTIFICATIONS权限才能显示通知

如果没有动态申请,即使Manifest里声明了权限,系统也会直接拒绝应用的相关请求。

三、NotificationBuilder的兼容性漏洞

你的showNotification方法只处理了Android O及以上的情况,低于O的版本完全没有初始化builder,会直接抛出空指针异常,导致通知无法显示。应该用NotificationCompat.Builder来兼容所有系统版本。

四、PendingIntent的高版本适配

Android 12(API 31)+ 要求PendingIntent必须指定FLAG_IMMUTABLEFLAG_MUTABLE,原来的FLAG_UPDATE_CURRENT需要和这两个flag结合使用,否则会抛出运行时异常。

五、异常捕获太宽泛,丢失调试信息

你在处理短信的try-catch里直接吞掉了所有异常,没有任何日志输出,出问题时根本无法定位错误原因。必须添加日志打印来记录异常细节。


修复后的完整代码

1. MainActivity(权限申请+动态广播注册)

class MainActivity : AppCompatActivity() {
    private lateinit var smsReceiver: SmsListener

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 先申请必要权限
        requestRequiredPermissions()

        // 初始化并注册短信广播接收器
        smsReceiver = SmsListener()
        val smsFilter = IntentFilter("android.provider.Telephony.SMS_RECEIVED")
        smsFilter.priority = 999 // 设置优先级,确保能优先收到广播(可选但实用)
        registerReceiver(smsReceiver, smsFilter)
    }

    private fun requestRequiredPermissions() {
        val permissionsToRequest = mutableListOf<String>()
        // 申请接收短信权限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED) {
            permissionsToRequest.add(Manifest.permission.RECEIVE_SMS)
        }
        // Android 13+ 申请通知权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
                permissionsToRequest.add(Manifest.permission.POST_NOTIFICATIONS)
            }
        }
        // 发起权限申请
        if (permissionsToRequest.isNotEmpty()) {
            ActivityCompat.requestPermissions(this, permissionsToRequest.toTypedArray(), 1001)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 注销接收器,避免内存泄漏
        unregisterReceiver(smsReceiver)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 1001) {
            // 可以在这里处理权限被拒绝的情况,比如弹窗提示用户开启权限
        }
    }
}

2. SmsListener广播接收器

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.Color
import android.os.Build
import android.provider.Telephony
import android.util.Log
import androidx.core.app.NotificationCompat

class SmsListener : BroadcastReceiver() {
    private val channelId = "i.apps.notifications"
    private val channelName = "New SMS Notifications"

    override fun onReceive(context: Context?, intent: Intent?) {
        context ?: return
        intent ?: return

        if (intent.action == Telephony.Sms.Intents.SMS_RECEIVED_ACTION) {
            val bundle = intent.extras ?: return
            try {
                val pdus = bundle["pdus"] as? Array<Any> ?: return
                val smsMessages = arrayOfNulls<Telephony.SmsMessage>(pdus.size)
                for (i in smsMessages.indices) {
                    smsMessages[i] = Telephony.SmsMessage.createFromPdu(pdus[i] as ByteArray)
                    val sender = smsMessages[i]?.originatingAddress ?: "Unknown Sender"
                    val messageBody = smsMessages[i]?.messageBody ?: ""
                    Log.d("SmsListener", "Received SMS from $sender: $messageBody")
                    
                    if (messageBody.isNotEmpty()) {
                        showNotification(context, messageBody, sender)
                    }
                }
            } catch (e: Exception) {
                // 打印异常日志,方便调试
                Log.e("SmsListener", "Failed to process SMS", e)
            }
        }
    }

    private fun showNotification(context: Context, message: String, sender: String) {
        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        // 创建通知渠道(仅Android O及以上需要)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationChannel = NotificationChannel(
                channelId,
                channelName,
                NotificationManager.IMPORTANCE_HIGH
            ).apply {
                lightColor = Color.BLUE
                enableVibration(true)
                description = "Notification for incoming SMS messages"
            }
            notificationManager.createNotificationChannel(notificationChannel)
        }

        // 跳转至消息详情页的Intent
        val detailIntent = Intent(context, MessageDetailsActivity::class.java).apply {
            putExtra(MessageDetailsActivity.KEY_MESSAGE, message)
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }

        // 适配Android 12+的PendingIntent Flag
        val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        } else {
            PendingIntent.FLAG_UPDATE_CURRENT
        }
        val pendingIntent = PendingIntent.getActivity(context, 0, detailIntent, pendingIntentFlags)

        // 使用NotificationCompat.Builder兼容所有版本
        val notificationBuilder = NotificationCompat.Builder(context, channelId)
            .setContentTitle("$sender: New message received")
            .setContentText(message.take(50))
            .setSmallIcon(R.drawable.common_google_signin_btn_icon_dark)
            .setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.ic_launcher_background))
            .setContentIntent(pendingIntent)
            .setPriority(NotificationCompat.PRIORITY_HIGH) // 高优先级确保通知弹出
            .setAutoCancel(true) // 点击通知后自动消失
            .setDefaults(Notification.DEFAULT_VIBRATE) // 开启默认震动

        // 发送通知
        notificationManager.notify(12345, notificationBuilder.build())
    }
}

3. AndroidManifest.xml(权限声明)

<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" /> <!-- 可选,如需读取更多短信信息 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" android:minSdkVersion="33" />

<!-- 移除原来的静态receiver注册 -->

额外注意事项

  • 如果应用被系统杀死,动态注册的广播接收器会失效。如果需要在应用完全关闭时也能接收短信,你需要改用前台服务结合动态广播,或者使用Google的SMS Retriever API(但需要短信包含特定格式的哈希值)。
  • 测试时请使用真实设备,模拟器的短信功能偶尔会有兼容性问题。
  • 确保你的应用没有被系统电池优化限制,可以在系统设置里关闭对应应用的电池优化。

内容的提问来源于stack exchange,提问作者Wai Yan Hein

火山引擎 最新活动