Chcę utworzyć usługę i uruchomić ją na pierwszym planie.
Większość przykładowych kodów zawiera powiadomienia. Ale nie chcę pokazywać żadnego powiadomienia. Czy to jest możliwe?
Czy możesz podać mi kilka przykładów? Czy są jakieś alternatywy?
Moja usługa aplikacji obsługuje odtwarzacz multimediów. Jak sprawić, by system nie zabijał mojej usługi, z wyjątkiem aplikacji, która sama ją zabija (np. Wstrzymywanie lub zatrzymywanie muzyki za pomocą przycisku).
android
android-service
foreground
Muhammad Resna Rizki Pratama
źródło
źródło
Odpowiedzi:
Ze względów bezpieczeństwa platformy Android nie można w żadnych okolicznościach mieć usługi pierwszoplanowej bez powiadomienia. Dzieje się tak, ponieważ usługa pierwszoplanowa zużywa większą ilość zasobów i podlega innym ograniczeniom związanym z planowaniem (tj. Nie zostaje tak szybko zabita) niż usługi działające w tle, a użytkownik musi wiedzieć, co prawdopodobnie zjada jego baterię. Więc nie rób tego rób tego.
Jednakże, to jest możliwe, aby mieć „fake” zawiadomienie, czyli można zrobić przezroczystą ikonę powiadomienia (IIRC). Jest to wyjątkowo nieszczere dla użytkowników i nie masz powodu, aby to robić, poza zabijaniem ich baterii i tworzeniem w ten sposób złośliwego oprogramowania.
źródło
Aktualizacja: problem został „naprawiony” w systemie Android 7.1. https://code.google.com/p/android/issues/detail?id=213309
Od aktualizacji 4.3 praktycznie niemożliwe jest uruchomienie usługi
startForeground()
bez wyświetlania powiadomienia.Możesz jednak ukryć ikonę za pomocą oficjalnych interfejsów API ... przezroczysta ikona nie jest potrzebna: (Użyj
NotificationCompat
do obsługi starszych wersji)NotificationCompat.Builder builder = new NotificationCompat.Builder(context); builder.setPriority(Notification.PRIORITY_MIN);
Pogodziłem się z faktem, że samo powiadomienie nadal musi tam być, ale dla każdego, kto nadal chce je ukryć, być może znalazłem również obejście tego problemu:
startForeground()
powiadomieniem i wszystkim.startForeground()
(tym samym identyfikatorem powiadomienia)stopSelf()
i zadzwonić do połączenia onDestroystopForeground(true)
).Voilà! Brak powiadomienia, a druga usługa nadal działa.
źródło
To już nie działa od Androida 7.1 i może naruszać zasady dla programistów Google Play .
Zamiast tego poproś użytkownika o zablokowanie powiadomienia o usłudze .
Oto mój Wdrożenie techniki w odpowiedzi przez Lior Iluz .
Kod
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" />
Zgodność
Przetestowane i pracuję nad:
Nie działa już od Androida 7.1.
źródło
ForegroundService
Możesz tego użyć (zgodnie z sugestią @Kristopher Micinski):
Notification note = new Notification( 0, null, System.currentTimeMillis() ); note.flags |= Notification.FLAG_NO_CLEAR; startForeground( 42, note );
AKTUALIZACJA:
Należy pamiętać, że nie jest to już dozwolone w wersjach Androida KitKat +. I pamiętaj, że jest to mniej więcej naruszenie zasady projektowania w Androidzie, która sprawia, że operacje w tle są widoczne dla użytkowników, jak wspomniał @Kristopher Micinski
źródło
Ostrzeżenie : chociaż wydaje się, że ta odpowiedź działa, w rzeczywistości po cichu zapobiega to, aby Twoja usługa stała się usługą pierwszego planu .
Oryginalna odpowiedź:
Po prostu ustaw identyfikator powiadomienia na zero:
// 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); ...
Korzyści, jakie możesz uzyskać, to: a
Service
będzie w stanie działać z wysokim priorytetem bez niszczenia przez system Android, chyba że przy dużym obciążeniu pamięci.EDYTOWAĆ
Aby działał z wersją Pre-Honeycomb i Androidem 4.4 i nowszymi wersjami, upewnij się, że używasz tego,
NotificationCompat.Builder
co zapewnia Support Library v7, a nieNotification.Builder
.źródło
Notification.Builder
, Support Library v4NotificationCompat.Builder
i Support Library v7NotificationCompat.Builder
. Powiadomienie nie pojawiło się w szufladzie powiadomień ani na pasku stanu, ale usługa nie działała w trybie pierwszego planu, gdy sprawdzałem ją przy użyciugetRunningServices()
idumpsys
.adb shell dumpsys activity services
do weryfikacji.Możesz ukryć powiadomienia w Androidzie 9+ , używając niestandardowego układu z layout_height = "0dp"
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.custom_notif); builder.setContent(remoteViews); builder.setPriority(NotificationCompat.PRIORITY_LOW); builder.setVisibility(Notification.VISIBILITY_SECRET);
custom_notif.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="0dp"> </LinearLayout>
Testowane na Pixelu 1, Androidzie 9. To rozwiązanie nie działa na Androidzie 8 lub starszym
źródło
Aktualizacja: to już nie działa w systemie Android 4.3 i nowszych
Istnieje jedno obejście. Spróbuj utworzyć powiadomienie bez ikony ustawień, a powiadomienie się nie pojawi. Nie wiem, jak to działa, ale działa :)
Notification notification = new NotificationCompat.Builder(this) .setContentTitle("Title") .setTicker("Title") .setContentText("App running") //.setSmallIcon(R.drawable.picture) .build(); startForeground(101, notification);
źródło
(YourServiceName) is running
powiadomienie zamiast własnego, jeśli nie określisz ikony. Według CommonsWare dzieje się tak od czasu Androida 4.3: commonsware.com/blog/2013/07/30/…Aktualizacja: to już nie działa w systemie Android 4.3 i nowszych
Ustawiłem parametr icon na konstruktor dla Notification na zero, a następnie przekazałem wynikowe powiadomienie do startForeground (). Brak błędów w dzienniku i brak powiadomień. Nie wiem jednak, czy usługa została pomyślnie uruchomiona - czy można to sprawdzić?
Edytowane: sprawdzone za pomocą dumpsys i rzeczywiście usługa jest na pierwszym planie w moim systemie 2.3. Nie sprawdzałem jeszcze z innymi wersjami systemu operacyjnego.
źródło
Notification()
konstruktor.Zablokuj powiadomienie usługi na pierwszym planie
Większość odpowiedzi tutaj nie działa, przerywa działanie usługi pierwszego planu lub narusza zasady Google Play .
Jedynym sposobem na niezawodne i bezpieczne ukrycie powiadomienia jest zablokowanie go przez użytkownika.
Android 4.1 - 7.1
Jedynym sposobem jest zablokowanie wszystkich powiadomień z Twojej aplikacji:
Wyślij użytkownika do ekranu szczegółów aplikacji:
Uri uri = Uri.fromParts("package", getPackageName(), null); Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri); startActivity(intent);
Miej powiadomienia aplikacji o blokowaniu użytkowników
Pamiętaj, że blokuje to również tosty Twojej aplikacji.
Android 8.0 - 8.1
Nie warto blokować powiadomienia na Androidzie O, ponieważ system operacyjny po prostu zastąpi je „ działającym w tle ” lub „ korzystającym z baterii ”.
Android 9+
Użyj kanału powiadomień, aby zablokować powiadomienie o usłudze bez wpływu na inne powiadomienia.
Wyślij użytkownika do ustawień kanału powiadomień
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS) .putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()) .putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId()); startActivity(intent);
Miej powiadomienia o blokowaniu użytkowników przez kanał
źródło
w wersji 4.3 (18) i nowszych ukrywanie powiadomienia serwisowego nie jest możliwe, ale można wyłączyć ikonę, w wersji 4.3 (18) i niższych można ukryć powiadomienie
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);
źródło
Odkryłem, że na Androidzie 8.0 jest to nadal możliwe, nie używając kanału powiadomień.
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 { //... } } } }
A w 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); } }
Trwałe powiadomienie nie jest wyświetlane, ale zobaczysz powiadomienie na Androida „x aplikacje działają w tle”.
źródło
apps are running in the background
powiadomienie jest jeszcze gorsze niż powiadomienie specyficzne dla aplikacji, więc nie jestem pewien, czy takie podejście jest tego warte.Aktualizacja: to już nie działa w systemie Android 7.1 i nowszych
Oto sposób na ustawienie oom_adj aplikacji na 1 (testowane w emulatorze ANDROID 6.0 SDK).Dodaj usługę tymczasową do głównego zgłoszenia serwisowego
startForgroundService(NOTIFICATION_ID, notificion)
. Następnie ponownie uruchom tymczasowestartForgroundService(NOTIFICATION_ID, notificion)
zgłoszenie serwisowe z tym samym identyfikatorem powiadomienia, po chwili w tymczasowym wywołaniu serwisowym stopForgroundService (true), aby odrzucić ontyfikację.źródło
Możesz również zadeklarować swoją aplikację jako trwałą.
<application android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/Theme" *android:persistent="true"* > </application>
To zasadniczo ustawia twoją aplikację na wyższy priorytet pamięci, zmniejszając prawdopodobieństwo jej zabicia.
źródło
Kilka miesięcy temu opracowałem prosty odtwarzacz multimedialny. Więc wierzę, że robisz coś takiego:
Intent i = new Intent(this, someServiceclass.class);
startService (i);
Wtedy system nie powinien być w stanie zabić twojej usługi.
odniesienie : przeczytaj akapit, w którym omówiono, kiedy system zatrzymuje usługę
źródło