You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 9.0+设备中BroadcastReceiver无法获取来电号码问题

这个问题我之前帮开发者排查过好几次,核心原因是Android 9(API 28)及以上版本的权限体系和电话状态监听机制发生了变化,导致你通过传统BroadcastReceiver拿不到来电号码。下面给你一步步拆解原因和解决方案:

问题根源

从Android 9开始,系统对来电号码的获取权限做了严格限制:

  • API 28(Android 9):READ_PHONE_STATE权限还能勉强拿到号码,但部分厂商的ROM已经提前收紧了权限;
  • API 29(Android 10)及以上:必须申请READ_CALL_LOG权限才能获取来电号码,READ_PHONE_STATE不再提供这个能力;
  • Android 12(API 31)+:还要额外申请READ_PHONE_NUMBERS权限。

另外,静态注册的BroadcastReceiver在高版本系统中,即使能触发,也可能因为权限优先级问题无法获取到Bundle里的号码数据。

解决方案

1. 先配置必要权限

在AndroidManifest.xml中添加权限声明:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<!-- Android 12+ 需添加 -->
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />

2. 动态申请危险权限

这些权限属于危险权限,必须在运行时向用户申请,不能只靠Manifest声明。示例代码:

private static final int PERMISSION_REQUEST_CODE = 101;

private void requestCallPermissions() {
    String[] requiredPermissions;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        requiredPermissions = new String[]{
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.READ_CALL_LOG,
            Manifest.permission.READ_PHONE_NUMBERS
        };
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        requiredPermissions = new String[]{Manifest.permission.READ_CALL_LOG};
    } else {
        requiredPermissions = new String[]{Manifest.permission.READ_PHONE_STATE};
    }

    // 检查权限是否已授予
    boolean allGranted = true;
    for (String perm : requiredPermissions) {
        if (ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) {
            allGranted = false;
            break;
        }
    }

    if (!allGranted) {
        ActivityCompat.requestPermissions(this, requiredPermissions, PERMISSION_REQUEST_CODE);
    }
}

3. 推荐:改用TelephonyCallback替代BroadcastReceiver

从Android 10开始,官方不再推荐用BroadcastReceiver监听电话状态,而是改用TelephonyCallback,它的可靠性更高,能直接拿到来电号码,不需要解析Bundle。示例代码:

private TelephonyManager telephonyManager;
private TelephonyCallback callStateCallback;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    
    // 创建电话状态回调
    callStateCallback = new TelephonyCallback() {
        @Override
        public void onCallStateChanged(int state, String phoneNumber) {
            super.onCallStateChanged(state, phoneNumber);
            switch (state) {
                case TelephonyManager.CALL_STATE_RINGING:
                    // 这里直接获取到来电号码
                    Log.d("IncomingCall", "来电号码:" + phoneNumber);
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    // 电话接通
                    break;
                case TelephonyManager.CALL_STATE_IDLE:
                    // 电话挂断或无通话
                    break;
            }
        }
    };

    // 检查权限后注册回调
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_GRANTED) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            telephonyManager.registerTelephonyCallback(getMainExecutor(), callStateCallback);
        } else {
            telephonyManager.registerTelephonyCallback(callStateCallback);
        }
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 记得注销回调,避免内存泄漏
    if (callStateCallback != null && telephonyManager != null) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            telephonyManager.unregisterTelephonyCallback(callStateCallback);
        } else {
            telephonyManager.unregisterTelephonyCallback(callStateCallback);
        }
    }
}

4. 如果坚持用BroadcastReceiver

如果一定要保留原有的BroadcastReceiver实现,除了确保权限已授予,建议改用动态注册的方式(静态注册在高版本系统中可能被限制):

private PhoneStateReceiver phoneStateReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    phoneStateReceiver = new PhoneStateReceiver();
    // 动态注册广播
    IntentFilter filter = new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
    registerReceiver(phoneStateReceiver, filter);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 注销广播
    unregisterReceiver(phoneStateReceiver);
}

// 自定义BroadcastReceiver
class PhoneStateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
            // 权限授予后就能拿到号码
            String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
            Log.d("IncomingCall", "来电号码:" + phoneNumber);
        }
    }
}
额外注意事项
  • 部分国产ROM会有额外的隐私限制,比如需要手动在应用权限设置中开启“读取通话记录”“读取电话状态”权限;
  • 测试时尽量用真实设备的来电,模拟器的模拟来电可能不会传递真实号码;
  • 确保权限申请成功后再进行监听,否则依然拿不到号码。

内容的提问来源于stack exchange,提问作者Het Thummar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 07:32:30