如何通过BroadcastReceiver匹配指定短信并替换短信通知铃声
实现特定短信自定义通知音并静音系统默认通知
嘿,我来帮你搞定这个需求!要实现「收到包含指定词汇的短信时,替换为SD卡音频并静音系统默认通知」的功能,需要从拦截系统广播、播放自定义音频、权限配置这三个核心点入手,下面一步步拆解并补全代码:
一、先配置必要权限
首先在AndroidManifest.xml中添加所需权限,同时给你的BroadcastReceiver设置高优先级(确保能先于系统短信应用收到广播):
<!-- 短信相关权限 --> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <!-- 访问SD卡音频文件权限(Android 13+可替换为READ_MEDIA_AUDIO) --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" android:minSdkVersion="33" /> <!-- 播放音频权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- 注册BroadcastReceiver,设置高优先级 --> <receiver android:name=".SmsCustomReceiver" android:exported="true"> <intent-filter android:priority="999"> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver>
注意:Android 6.0及以上,
RECEIVE_SMS、READ_EXTERNAL_STORAGE属于危险权限,需要在代码中动态申请,用户授权后才能生效。
二、补全BroadcastReceiver的核心逻辑
在你现有代码的基础上,添加拦截系统广播和播放自定义音频的逻辑:
public class SmsCustomReceiver extends BroadcastReceiver { private MediaPlayer mMediaPlayer; @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) { final Bundle bundle = intent.getExtras(); try { if (bundle != null) { final Object[] pdusObj = (Object[]) bundle.get("pdus"); for (int i = 0; i < pdusObj.length; i++) { SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]); String address = currentMessage.getDisplayOriginatingAddress(); String smsBody = currentMessage.getDisplayMessageBody(); // 检查短信内容是否包含指定词汇 if (smsBody.contains("hello")) { // 1. 拦截系统默认通知:终止广播传递,阻止系统短信应用处理 abortBroadcast(); // 2. 播放SD卡中的自定义音频 playCustomSound(context); } } } } catch (Exception e) { e.printStackTrace(); } } } private void playCustomSound(Context context) { // 替换为你SD卡中音频文件的路径,比如根目录下的custom_notify.mp3 String soundPath = Environment.getExternalStorageDirectory().getPath() + "/custom_notify.mp3"; File soundFile = new File(soundPath); if (!soundFile.exists()) { Toast.makeText(context, "自定义音频文件不存在", Toast.LENGTH_SHORT).show(); return; } // 初始化MediaPlayer播放音频 if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.release(); } mMediaPlayer = new MediaPlayer(); try { mMediaPlayer.setDataSource(soundPath); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION); mMediaPlayer.prepare(); mMediaPlayer.start(); // 播放完毕后释放资源 mMediaPlayer.setOnCompletionListener(mp -> { mp.release(); mMediaPlayer = null; }); } catch (IOException e) { e.printStackTrace(); Toast.makeText(context, "播放音频失败", Toast.LENGTH_SHORT).show(); } } }
三、关键细节说明
- 拦截系统广播:
abortBroadcast()只能用于有序广播,而SMS_RECEIVED_ACTION正是有序广播,设置高优先级后我们的Receiver会先收到广播,调用这个方法就能阻止后续的Receiver(比如系统短信应用)处理,从而静音系统默认通知。 - 自定义音频播放:使用
MediaPlayer并指定STREAM_NOTIFICATION音频流,确保和系统通知音使用同一流,不会干扰其他音频播放;播放完毕后记得释放资源,避免内存泄漏。 - Android版本兼容:
- Android 10+访问外部存储时,直接用
Environment.getExternalStorageDirectory()可能受限,建议使用MediaStore查询音频文件,或者将音频放在应用专属外部目录(context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)),这样无需额外权限。 - Android 13+需要申请
READ_MEDIA_AUDIO权限替代READ_EXTERNAL_STORAGE。
- Android 10+访问外部存储时,直接用
四、动态申请权限示例
在你的MainActivity中添加动态权限申请逻辑:
private static final int REQUEST_PERMISSIONS = 1001; private String[] permissions = { Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS, Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ? Manifest.permission.READ_MEDIA_AUDIO : Manifest.permission.READ_EXTERNAL_STORAGE }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(permissions[0]) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(permissions[1]) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(permissions[2]) != PackageManager.PERMISSION_GRANTED) { requestPermissions(permissions, REQUEST_PERMISSIONS); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PERMISSIONS) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (!allGranted) { Toast.makeText(this, "请授予必要权限以使用功能", Toast.LENGTH_SHORT).show(); } } }
内容的提问来源于stack exchange,提问作者user5050715
相关产品推荐
相关产品推荐

