Android 5及以上版本接听/拒接来电实现求助
解决Android来电自定义接听按钮无效的问题
首先,你的核心问题在于高版本Android对电话权限和系统API的限制,以及旧的接听方法已经失效。下面我会一步步帮你修复:
1. 权限适配(Android 6+)
首先,你需要动态申请必要的权限——静态声明的权限在Android 6+需要用户手动授权,尤其是ANSWER_PHONE_CALLS和READ_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()方法:
修改IncomingActivity的acceptCall()方法:
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(); } }
同时,在IncomingActivity的onCreate()中添加窗口标记,确保来电时能在锁屏上显示:
@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.




