Как startForeground () без отображения уведомления?

Я хочу создать сервис и запустить его на переднем плане.

большинство примеров кодов уведомления на нем. Но я не хочу показывать никаких уведомлений. Это возможно?

можете привести несколько примеров? Есть ли альтернативы?

моя служба приложений делает mediaplayer. как сделать систему не убить мой сервис, кроме приложения убить его самого (например, приостановка или остановка музыки кнопкой).

13 ответов


в качестве функции безопасности платформы Android, вы не может, под любой обстоятельство, имейте переднюю службу без также иметь уведомление. Это связано с тем, что служба переднего плана потребляет большее количество ресурсов и подвержена различным ограничениям планирования (т. е. она не убивается так быстро), чем фоновые службы, и пользователь должен знать, что, возможно, ест свою батарею. Итак,не сделать этот.

, Это is возможно иметь "поддельное" уведомление, т. е. вы можете сделать прозрачный значок уведомления (iirc). Это крайне неискренне для ваших пользователей, и у вас нет причин делать это, кроме как убивать их батарею и, таким образом, создавать вредоносные программы.

обновление: это было "исправлено" на Android 7.1. https://code.google.com/p/android/issues/detail?id=213309

начиная с обновления 4.3, это в основном невозможно чтобы начать службу с startForeground() Не показывать уведомления.

вы можете, однако, скрыть значок с помощью официальных API... нет необходимости в прозрачном значке: (Используйте NotificationCompat для поддержки старых версий)

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MIN);

я смирился с фактом само уведомление по-прежнему должно быть там, но для тех, кто все еще хочет его скрыть, я, возможно, нашел обходной путь для этого:

  1. начните поддельный сервис с startForeground() с уведомлением и все.
  2. запустите реальный сервис, который вы хотите запустить, также с startForeground() (тот же идентификатор уведомления)
  3. остановить первый (поддельный) сервис (вы можете позвонить stopSelf() и в onDestroy вызов stopForeground(true)).

вуаля! Никакие уведомления вообще и ваша вторая служба продолжает работать.


вот моя реализация техники в ответ by Лиор Илуз.

обратите внимание, что это больше не работает в Android 7.1. Также обратите внимание, что этот метод потенциально может быть истолковано как нарушение политики разработчика Google Play (Apps that introduce or exploit security vulnerabilities.).

код

ForegroundService.java

public class ForegroundService extends Service {

    static ForegroundService instance;

    @Override
    public void onCreate() {
        super.onCreate();

        instance = this;

        if (startService(new Intent(this, ForegroundEnablingService.class)) == null)
            throw new RuntimeException("Couldn't find " + ForegroundEnablingService.class.getSimpleName());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        instance = null;
    }

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

}

ForegroundEnablingService.java

public class ForegroundEnablingService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (ForegroundService.instance == null)
            throw new RuntimeException(ForegroundService.class.getSimpleName() + " not running");

        //Set both services to foreground using the same notification id, resulting in just one notification
        startForeground(ForegroundService.instance);
        startForeground(this);

        //Cancel this service's notification, resulting in zero notifications
        stopForeground(true);

        //Stop this service so we don't waste RAM.
        //Must only be called *after* doing the work or the notification won't be hidden.
        stopSelf();

        return START_NOT_STICKY;
    }

    private static final int NOTIFICATION_ID = 10;

    private static void startForeground(Service service) {
        Notification notification = new Notification.Builder(service).getNotification();
        service.startForeground(NOTIFICATION_ID, notification);
    }

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

}

AndroidManifest.в XML

<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />

совместимость

протестировано и работает над:

  • официальный Эмулятор
    • 4.0.2
    • 4.1.2
    • 4.2.2
    • 4.3.1
    • 4.4.2
    • 5.0.2
    • 5.1.1
    • 6.0
    • 7.0
  • Sony Xperia M
    • 4.1.2
    • 4.3
  • Samsung Galaxy ?
    • 4.4.2
    • 5.Икс
  • как Genymotion
    • 5.0
    • 6.0
  • в CyanogenMod
    • 5.1.1

больше не работает с Android 7.1.


вы можете использовать это :

    Notification note = new Notification( 0, null, System.currentTimeMillis() );
    note.flags |= Notification.FLAG_NO_CLEAR;
    startForeground( 42, note );

как предложил @Kristopher Micinski

- - - - - UPDATE

обратите внимание, что это больше не разрешено с выпусками Android KitKat+. И имейте в виду, что это более или менее нарушает принцип дизайна в Android, который делает фоновые операции видимыми для пользователей, как упоминалось @Kristopher Micinski


просто установите идентификатор уведомления в ноль:

// field for notification ID
private static final int NOTIF_ID = 0;

    ...
    startForeground(NOTIF_ID, mBuilder.build());
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    mNotificationManager.cancel(NOTIF_ID);
    ...

преимущество, которое вы можете получить, это Service будет в состоянии работает на высоком приоритете без уничтожены системой Android, если на высоком давлении памяти.

редактировать

чтобы он работал с Pre-Honeycomb и Android 4.4 и выше, убедитесь, что вы используете NotificationCompat.Builder который предоставляется библиотекой поддержки v7, а не Notification.Builder.


Я установил параметр icon в конструктор для уведомления в ноль, а затем передал полученное уведомление в startForeground(). Никаких ошибок в журнале и никаких уведомлений. Я не знаю, однако, была ли служба успешно выдвинута на передний план-есть ли способ проверить?

Edited: проверено с помощью dumpsys, и действительно, служба находится на переднем плане в моей системе 2.3. Еще не проверял с другими версиями ОС.


есть одно решение. Попробуйте создать уведомление без значка настройки, и уведомление не будет отображаться. Не знаю, как это работает, но это делает :)

    Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle("Title")
            .setTicker("Title")
            .setContentText("App running")
            //.setSmallIcon(R.drawable.picture)
            .build();
    startForeground(101,  notification);

Я нашел на 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.класс:

 @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 приложения работают в фоновом режиме" уведомления.


версия 4.3 (18) и выше скрыть уведомление службы невозможно , но вы можете отключить значок , версия 4.3(18) и ниже можно скрыть уведомление

Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
    noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);

блокировать уведомление службы переднего плана

начиная с Android 7.1, кажется, нет никаких трюков/ошибок, которые вы можете использовать для этого. Поэтому вместо этого вам нужно указать пользователю отключить уведомление службы переднего плана в настройках Android.

как

Android 4.1 - 7.1 (включительно)

Я считаю, что единственный способ-отключить все уведомлений приложения.

  1. использовать намерение отправить пользователя на экран сведений о вашем приложении:

    Uri uri = Uri.fromParts("package", getPackageName(), null);
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
    startActivity(intent);
    
  2. затем пользователь блокирует уведомления приложения оттуда.

обратите внимание, что это также блокирует тосты вашего приложения.

Android 8:

к сожалению, если пользователь блокирует уведомление службы, то Android заменит его на "{имя приложения} работает в фоновом режиме" уведомления. Так что нет смысла скрывать он.


вот способ сделать ваше приложение oom_adj до 1 (протестировано в эмуляторе Android 6.0 SDK). Добавьте временную услугу, в основной вызов службы startForgroundService(NOTIFICATION_ID, notificion). А затем запустите вызов временной службы startForgroundService(NOTIFICATION_ID, notificion) с тем же идентификатором уведомления снова, через некоторое время во временной службе вызовите stopForgroundService(true), чтобы отклонить onging ontification.


пару месяцев назад я разработал простой медиаплеер. Так что я считаю, если вы делаете что-то вроде:

Intent i = new Intent(this, someServiceclass.class);

startService (i);

тогда система не должна быть в состоянии убить вашу службу.

ссылка: прочитайте абзац, который обсуждает, когда система останавливает службу


вы также можете объявить свое приложение постоянным.

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

это по существу устанавливает ваше приложение на более высокий приоритет памяти, уменьшая вероятность его гибели.