如何在不显示通知的情况下启动 startForeground()?

110

我想创建一个服务并使它在前台运行。

大多数示例代码都带有通知,但我不想显示任何通知。这可能吗?

你能给我一些例子吗?还有其他替代方案吗?

我的应用服务正在执行播放器功能。如何让系统不杀死我的服务,除非应用程序自己终止它(例如通过按钮暂停或停止音乐)?


13
没有通知,你就无法创建一个“前台”服务。没得商量。 - Jug6ernaut
1
怎么样发送一个“假”的通知?这是一个技巧吗? - Muhammad Resna Rizki Pratama
一旦设置了前台通知,您可以使用Rubber应用程序删除“在后台运行的通知”。 - Laurent
1
@MuhammadResnaRizkiPratama,你是否考虑更改接受的答案?这个问题非常受欢迎,但接受的答案已经过时、过于强硬,并且假设开发者需要它。我建议使用这个答案,因为它可靠,不利用操作系统漏洞,并且适用于大多数Android设备。 - Sam
16个回答

3
我发现在Android 8.0上,仍然可以不使用通知渠道来实现。
public class BootCompletedIntentReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

                Intent notificationIntent = new Intent(context, BluetoothService.class);    
                context.startForegroundService(notificationIntent);

            } else {
                //...
            }

        }
    }
}

还有在 BluetoothService.class 中:

 @Override
    public void onCreate(){    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            Intent notificationIntent = new Intent(this, BluetoothService.class);

            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

            Notification notification = new Notification.Builder(this)
                    .setContentTitle("Title")
                    .setContentText("App is running")
                    .setSmallIcon(R.drawable.notif)
                    .setContentIntent(pendingIntent)
                    .setTicker("Title")
                    .setPriority(Notification.PRIORITY_DEFAULT)
                    .build();

            startForeground(15, notification);

        }

    }

虽然不会出现持续性通知,但你会看到Android系统的“x个应用程序正在后台运行”通知。


1
在Android 8.1中:"startForeground的通知有问题:java.lang.RuntimeException:服务通知的通道无效" - T-igra
个人认为,“后台应用正在运行”通知甚至比特定应用程序的通知更糟糕,因此我不确定这种方法是否值得。 - Sam

2
尽管这不是一个直接的问题,但许多正在搜索的人可以通过使用WorkManager轻松解决创建持久的、长时间运行的任务的挑战。
截至2022年,这是谷歌推荐的用于运行各种类型后台任务(一次性、周期性、加速、前台等)的API。

1

有时客户不想让前台服务一直显示通知,这对开发人员来说是很麻烦的。我创建了一个假通知来启动服务,然后通过notificationManager.cancel(1);取消它。

  final String NOTIFICATION_CHANNEL_ID = "com.exmaple.project";
    final String channelName = "Notification";
   @RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onCreate() {
    super.onCreate();
    stopForeground(true);
    Intent stopSelf = new Intent(this, Notification_Service.class);
    stopSelf.setAction("ACTION_STOP_SERVICE");
    PendingIntent pStopSelf = PendingIntent
            .getService(this, 0, stopSelf
                    , PendingIntent.FLAG_CANCEL_CURRENT);
    Notification notification;
    NotificationCompat.Action action =
            new NotificationCompat.Action.Builder(
                    0, "Close", pStopSelf
            ).build();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel serviceChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Notification One", NotificationManager.IMPORTANCE_DEFAULT);
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(serviceChannel);
        notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentText("Welcome to App.")
                .setPriority(Notification.PRIORITY_MIN)
                .addAction(action)
                .build();
    } else {
        notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentTitle("App")
                .setContentText("Welcome to App.")
                .setPriority(Notification.PRIORITY_MIN)
                .addAction(action)
                .build();
    }
    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Service.NOTIFICATION_SERVICE);
    notificationManager.notify(1, notification);
    startForeground(1, notification);
    notificationManager.cancel(1);
}

有时候,通过notificationManager.cancel(1);无法移除永久通知。为此,我添加了一个虚假的关闭操作按钮。 操作按钮结果:
 @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
            if ("ACTION_STOP_SERVICE".equals(intent.getAction())) {
                stopSelf();
            }
     
            return START_STICKY;
        }

开始服务:
 if (!isMyServiceRunning()) {
       Intent serviceIntent = new Intent(this, Notification_Service.class);
                ContextCompat.startForegroundService(this, serviceIntent);
            }

检查服务是否已经在运行。

private boolean isMyServiceRunning() {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (Notification_Service.class.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

0
最合适的解决方案是使用通知渠道(Notification Channel)。
你所需要做的就是从你的类中删除notificationManager.createNotificationChannel(channel)
val notificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val channel = NotificationChannel(
            notificationChannelId,
            "Endless Service notifications channel",
            NotificationManager.IMPORTANCE_HIGH
        ).let {
            it.description = "Endless Service channel"
            it.enableLights(true)
            it.lightColor = Color.RED
            it.enableVibration(true)
            it.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
            it
        }
        notificationManager.createNotificationChannel(channel)

OR

通过简单地使用notificationManager.deleteNotificationChannel("channel_id")即可实现。

但是,不建议删除正在被前台服务使用的通知。


-1

更新:在 Android 7.1 及以上版本中,此链接不再可用


这里有一种方法可以将您的应用程序的 oom_adj 设置为 1(在 ANDROID 6.0 SDK 模拟器中测试过)。添加一个临时服务,在您的主服务中调用 startForgroundService(NOTIFICATION_ID, notificion)。然后再次使用相同的通知 ID 调用临时服务中的 startForgroundService(NOTIFICATION_ID, notificion),一段时间后在临时服务中调用 stopForgroundService(true) 来关闭正在进行的通知。


这是我所知道的唯一可行的解决方案。然而,它已经被另一个答案涵盖了。 - Sam

-2

你也可以将你的应用程序声明为持久化。

<application
    android:icon="@drawable/icon"
    android:label="@string/app_name"
    android:theme="@style/Theme"
    *android:persistent="true"* >
</application>

这实际上将您的应用程序设置为更高的内存优先级,降低了其被终止的可能性。


5
这是错误的,只有系统应用程序才能保持持久状态:https://groups.google.com/forum/?fromgroups=#!topic/android-developers/2E10ZSnaK2Q - Snicolas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接