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

Android 5及以上版本接听/拒接来电实现求助

解决Android来电自定义接听按钮无效的问题

首先,你的核心问题在于高版本Android对电话权限和系统API的限制,以及旧的接听方法已经失效。下面我会一步步帮你修复:

1. 权限适配(Android 6+)

首先,你需要动态申请必要的权限——静态声明的权限在Android 6+需要用户手动授权,尤其是ANSWER_PHONE_CALLSREAD_PHONE_STATE

// 在启动监听的Activity或Service中添加权限申请逻辑
private static final int REQUEST_PHONE_PERMISSIONS = 100;

private void requestPhonePermissions() {
    String[] permissions = {
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.ANSWER_PHONE_CALLS,
            Manifest.permission.DISABLE_KEYGUARD,
            Manifest.permission.WAKE_LOCK
    };
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED ||
        ContextCompat.checkSelfPermission(this, Manifest.permission.ANSWER_PHONE_CALLS) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, permissions, REQUEST_PHONE_PERMISSIONS);
    }
}

2. 修复BroadcastReceiver的监听问题(Android 8+)

Android 8.0(API 26)之后,静态注册的BroadcastReceiver无法接收大部分隐式广播(包括PHONE_STATE),所以你需要动态注册Receiver,建议放在前台Service中,避免被系统后台杀死:

public class PhoneMonitorService extends Service {
    private PhoneListenerBroad phoneReceiver;

    @Override
    public void onCreate() {
        super.onCreate();
        // 启动前台Service,防止被系统回收
        Notification notification = new NotificationCompat.Builder(this, "phone_monitor_channel")
                .setSmallIcon(R.drawable.ic_notification)
                .setContentTitle("来电监听服务")
                .setContentText("正在监听来电状态")
                .build();
        startForeground(1, notification);

        // 动态注册来电监听Receiver
        phoneReceiver = new PhoneListenerBroad();
        IntentFilter filter = new IntentFilter();
        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
        filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
        registerReceiver(phoneReceiver, filter);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (phoneReceiver != null) {
            unregisterReceiver(phoneReceiver);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

别忘了在Manifest中注册这个Service,并添加前台服务权限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<service android:name=".Services.PhoneMonitorService"
         android:foregroundServiceType="phoneCall"/>

3. 修改接听电话的实现(Android 8+有效)

你之前用的模拟耳机按键的方法在Android 8+已经被弃用且失效,应该使用官方推荐的TelecomManager.acceptRingingCall()方法:

修改IncomingActivityacceptCall()方法:

public void acceptCall() {
    TelecomManager telecomManager = (TelecomManager) getSystemService(Context.TELECOM_SERVICE);
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ANSWER_PHONE_CALLS) != PackageManager.PERMISSION_GRANTED) {
        // 权限未授权时先申请
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ANSWER_PHONE_CALLS}, 101);
        return;
    }
    try {
        telecomManager.acceptRingingCall();
        finish(); // 接听后关闭自定义Activity
    } catch (SecurityException e) {
        e.printStackTrace();
        Toast.makeText(this, "接听失败:权限不足", Toast.LENGTH_SHORT).show();
    }
}

同时,在IncomingActivityonCreate()中添加窗口标记,确保来电时能在锁屏上显示:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 确保锁屏状态下能显示自定义来电页面
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
            | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
            | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
            | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
    setContentView(R.layout.activity_incoming);
    
    // 原有按钮初始化逻辑...
}

4. 优化BroadcastReceiver的逻辑

你的PhoneListenerBroad中每次收到广播都重新注册PhoneStateListener会导致重复监听,建议修改为只注册一次:

public class PhoneListenerBroad extends BroadcastReceiver {
    private MyPhoneStateListener phoneStateListener;
    private TelephonyManager telephonyManager;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (telephonyManager == null) {
            telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            phoneStateListener = new MyPhoneStateListener(context);
            telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
        }

        if (Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())) {
            String outgoing = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            Intent intentPhoneCall = new Intent(context, IncomingActivity.class);
            intentPhoneCall.putExtra("incomingnumber", outgoing);
            intentPhoneCall.putExtra("state", 2);
            intentPhoneCall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intentPhoneCall);
        }
    }

    private class MyPhoneStateListener extends PhoneStateListener {
        private Context context;
        private Handler handler = new Handler();
        private Runnable showActivityRunnable;

        public MyPhoneStateListener(Context context) {
            this.context = context;
        }

        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            super.onCallStateChanged(state, incomingNumber);
            if (state == TelephonyManager.CALL_STATE_RINGING) {
                showActivityRunnable = () -> {
                    Intent intentPhoneCall = new Intent(context, IncomingActivity.class);
                    intentPhoneCall.putExtra("incomingnumber", incomingNumber);
                    intentPhoneCall.putExtra("state", state);
                    intentPhoneCall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    context.startActivity(intentPhoneCall);
                };
                handler.postDelayed(showActivityRunnable, 100);
            } else if (state == TelephonyManager.CALL_STATE_IDLE) {
                if (showActivityRunnable != null) {
                    handler.removeCallbacks(showActivityRunnable);
                }
            }
        }
    }
}

最后注意事项

  • Android 11+对电话权限的限制更严格,确保你的APP在系统权限设置中开启了所有电话相关权限。
  • 自定义来电Activity的UI尽量简洁,避免被系统判定为恶意弹窗。
  • 测试时建议使用真机,模拟器的电话功能可能存在兼容性问题。

内容的提问来源于stack exchange,提问作者İsa C.

火山引擎 最新活动