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

Android Telephony Manager来电监听数小时后失效问题求助

解决Android Telephony Manager广播监听数小时失效的思路

我之前也碰到过类似的Telephony广播失效问题,结合你的场景和代码,给你几个针对性的解决方向:


1. 改用「前台服务+动态广播注册」替代静态注册

Android 8.0+对后台广播和服务的限制非常严格,静态注册的广播很容易被系统回收,普通后台服务也会被优先查杀。把广播接收器放在前台服务里动态注册是最有效的解决方案——前台服务有系统优先级加持,只要显示必要的通知,就很难被系统杀死。

示例代码框架:

public class CallRecorderService extends Service {
    private TelephonyBroadcastReceiver receiver;
    private PowerManager.WakeLock wakeLock;

    @Override
    public void onCreate() {
        super.onCreate();
        // 必须启动前台通知(Android强制要求)
        Notification notification = createForegroundNotification();
        startForeground(1001, notification);

        // 动态注册通话广播
        receiver = new TelephonyBroadcastReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
        filter.addAction("android.intent.action.NEW_OUTGOING_CALL");
        registerReceiver(receiver, filter);

        // 初始化唤醒锁(后续优化用)
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "CallRecorder:WakeLock");
    }

    // 自定义前台通知(简单示例)
    private Notification createForegroundNotification() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("call_recorder", "通话录音服务", NotificationManager.IMPORTANCE_LOW);
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
        return new NotificationCompat.Builder(this, "call_recorder")
                .setSmallIcon(R.drawable.ic_notification)
                .setContentTitle("通话录音服务运行中")
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .build();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 销毁时释放资源
        if (receiver != null) unregisterReceiver(receiver);
        if (wakeLock != null && wakeLock.isHeld()) wakeLock.release();
    }

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

2. 优化唤醒锁的使用逻辑

你之前用唤醒锁没效果,大概率是锁的类型不对或者生命周期管理有问题:

  • 必须使用PARTIAL_WAKE_LOCK(保持CPU唤醒,屏幕可以关闭),其他类型的锁在通话场景下不适用;
  • 不要在广播接收器里持有锁(Receiver生命周期极短,锁还没生效就被释放了),应该在Service里,当通话进入OFFHOOK状态时获取锁,回到IDLE状态时释放锁。

示例代码片段:

// 在Service的通话状态处理方法中调用
private void handleCallState(int state) {
    if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
        // 通话开始,获取唤醒锁防止CPU休眠
        if (!wakeLock.isHeld()) wakeLock.acquire();
    } else if (state == TelephonyManager.CALL_STATE_IDLE) {
        // 通话结束,释放唤醒锁
        if (wakeLock.isHeld()) wakeLock.release();
    }
}

3. 用WorkManager替代Timer/AlarmManager做健康检查

Timer和普通AlarmManager在Doze模式下会被系统限制,改用WorkManager来周期性检查服务状态更可靠——它会自动适配系统的省电策略,在设备退出Doze、有电量时执行任务。

比如每1小时检查一次录音服务是否在运行,如果失效就重启:

public class CheckServiceWorker extends Worker {
    public CheckServiceWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        boolean isServiceRunning = isMyServiceRunning(CallRecorderService.class);
        if (!isServiceRunning) {
            Intent intent = new Intent(getApplicationContext(), CallRecorderService.class);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                getApplicationContext().startForegroundService(intent);
            } else {
                getApplicationContext().startService(intent);
            }
        }
        return Result.success();
    }

    private boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
}

在应用启动时调度这个任务:

PeriodicWorkRequest checkServiceRequest = new PeriodicWorkRequest.Builder(CheckServiceWorker.class, 1, TimeUnit.HOURS)
        .setConstraints(new Constraints.Builder()
                .setRequiresBatteryNotLow(true)
                .build())
        .build();
WorkManager.getInstance(context).enqueueUniquePeriodicWork("CheckCallRecorderService", ExistingPeriodicWorkPolicy.REPLACE, checkServiceRequest);

4. 适配厂商后台限制

国内小米、华为、OPPO等厂商有自己的后台管理机制,会主动杀死无权限的后台应用。你需要引导用户做以下设置:

  • 给应用开启自启动权限;
  • 在省电策略中设置为「无限制」;
  • 将应用加入系统后台白名单。

这一步是刚需,很多时候代码没问题,就是厂商的限制导致服务被查杀。


5. 轻量化广播接收器逻辑

看你贴的代码,每次收到广播都初始化RoPlantPrefs和DatabaseHelper,还做数据库操作——Receiver的生命周期只有几毫秒,复杂操作很容易触发ANR,进而导致进程被系统杀死。

建议把Receiver简化成「事件转发器」,只负责把通话事件传递给Service处理,所有复杂逻辑(数据库、状态判断)都放在Service中:

@Override 
public void onReceive(Context context, Intent intent) { 
    RoPlantPrefs prefs = RoPlantPrefs.getInstance(context);
    if (!prefs.getLogin()) return;

    // 转发事件到Service
    Intent serviceIntent = new Intent(context, CallRecorderService.class);
    serviceIntent.putExtras(intent.getExtras());
    serviceIntent.setAction(intent.getAction());
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(serviceIntent);
    } else {
        context.startService(serviceIntent);
    }
}

内容的提问来源于stack exchange,提问作者Jhaman Das

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:04:14