Android Oreo系统下无法获取呼出号码问题求助
解决Android Oreo无法获取呼出号码的问题
我来帮你搞定这个在Oreo系统上拿不到呼出号码的问题,核心原因是Android 8.0(API 26)对广播接收器的权限和注册方式做了严格限制,你的老代码在低版本能正常运行,到Oreo就失效了,具体原因和修复方案如下:
问题根源
在Oreo之前,静态注册的BroadcastReceiver可以正常接收android.intent.action.NEW_OUTGOING_CALL广播,但从Android 8.0开始,系统禁止静态注册的接收器接收大部分隐式广播(包括这个呼出电话广播),你的场景刚好不在例外范围内。另外,你在onReceive里嵌套注册PhoneStateListener的做法也不严谨,容易导致内存泄漏和重复监听的问题。
分步修复方案
1. 替换静态注册为动态注册广播接收器
Oreo及以上系统必须动态注册NEW_OUTGOING_CALL的接收器,不能再依赖Manifest里的静态注册。比如在你的主Activity或者Application类里完成注册:
public class MainActivity extends AppCompatActivity { private CallBroadcastReceiver callReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化并动态注册广播接收器 callReceiver = new CallBroadcastReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); registerReceiver(callReceiver, filter); } @Override protected void onDestroy() { super.onDestroy(); // 一定要注销接收器,避免内存泄漏 if (callReceiver != null) { unregisterReceiver(callReceiver); } } }
2. 重构广播接收器逻辑
把来电和呼出的监听逻辑整合到一个更简洁的接收器里,去掉嵌套的PhoneStateListener(呼出电话直接通过广播处理就足够了):
首先确保Manifest里声明了必要权限:
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
然后修改接收器代码:
public class CallBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action == null) return; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = prefs.edit(); String targetNumber = null; switch (action) { // 处理来电逻辑 case TelephonyManager.ACTION_PHONE_STATE_CHANGED: targetNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER); break; // 处理呼出电话逻辑 case Intent.ACTION_NEW_OUTGOING_CALL: targetNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); break; } if (targetNumber != null) { editor.putString("number", targetNumber); editor.apply(); initialiseAndStartService(context); } } private void initialiseAndStartService(Context context) { // 注意:Oreo之后启动后台服务会被限制,需要改成前台服务 Intent serviceIntent = new Intent(context, YourTargetService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); } } }
3. 适配Oreo的前台服务要求
如果你的initialiseAndStartService启动的是普通后台服务,在Oreo及以上系统会被系统强制停止,所以要把服务改成前台服务,并创建通知通道(API 26+强制要求):
在你的服务类里添加:
@Override public void onCreate() { super.onCreate(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 创建通知通道 String channelId = "CallService_Channel"; NotificationChannel channel = new NotificationChannel( channelId, "Call Processing Service", NotificationManager.IMPORTANCE_LOW // 低优先级,不会打扰用户 ); NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); // 启动前台服务 Notification notification = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.drawable.ic_service_icon) .setContentTitle("正在处理通话") .setPriority(NotificationCompat.PRIORITY_LOW) .build(); startForeground(1001, notification); } }
额外提示
- 动态注册的接收器会在宿主组件(比如Activity)销毁时失效,所以如果需要全局监听,可以考虑在
Application类里注册,但要注意内存泄漏问题。 - 测试时记得给应用授予必要的电话权限,Oreo之后权限需要动态申请,别忘了加权限申请的逻辑哦。
内容的提问来源于stack exchange,提问作者yasir khan
相关产品推荐
相关产品推荐

