Android - StartForeground für einen Dienst implementieren?

124

Ich bin mir also nicht sicher, wo / wie ich diese Methode implementieren soll, damit mein Dienst im Vordergrund ausgeführt wird. Derzeit beginne ich meinen Dienst wie folgt in einer anderen Aktivität:

Intent i = new Intent(context, myService.class); 
context.startService(i);

Und dann versuche ich in onCreate () von myServices startForeground () ...?

Notification notification = new Notification();
startForeground(1, notification);

Also ja, ich bin ein bisschen verloren und unsicher, wie ich das umsetzen soll.

JDS
quelle
Nun, das funktioniert nicht, zumindest soweit ich das beurteilen kann, funktioniert mein Dienst immer noch als Hintergrunddienst und wird getötet.
JDS
Thread ist verlinkt mit: stackoverflow.com/questions/10962418/…
Snicolas

Antworten:

131

Ich würde damit beginnen, das vollständig auszufüllen Notification. Hier ist ein Beispielprojekt , das die Verwendung von demonstriert startForeground().

CommonsWare
quelle
8
Ist es möglich, startForeground () ohne Benachrichtigung zu verwenden? Oder können wir dieselbe Benachrichtigung später aktualisieren?
GFS
2
Gibt es einen bestimmten Grund, den Sie verwendet haben 1337?
Cody
33
@DoctorOreo: Es muss innerhalb der App eindeutig sein, jedoch nicht unbedingt auf dem Gerät. Ich habe 1337 gewählt, weil es 1337 ist . :-)
CommonsWare
@ JCR Frage ist eine gute. Ist es möglich, startForeground () ohne Benachrichtigung zu verwenden?
Snicolas
2
@Snicolas: Vielen Dank, dass Sie auf einen Fehler in Android hingewiesen haben. Ich werde daran arbeiten, dies zu beheben.
CommonsWare
78

Starten Sie den Dienst von Ihrer Hauptaktivität aus mit dem folgenden Code:

Intent i = new Intent(context, MyService.class); 
context.startService(i);

Dann würden Sie in Ihrem Dienst für onCreate()Sie Ihre Benachrichtigung erstellen und sie wie folgt in den Vordergrund stellen:

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

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

Notification notification = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.app_icon)
                .setContentTitle("My Awesome App")
                .setContentText("Doing some work...")
                .setContentIntent(pendingIntent).build();

startForeground(1337, notification);
mikebertiean
quelle
@mike Wie aktualisiere ich diese Benachrichtigung von MainActivity?
Roon13
1
@ Roon13 unter Verwendung der ID, in diesem Fall 1337 ... Sie sollten in der Lage sein, eine neue Benachrichtigung zu erstellen und startForeground mit der ID
aufzurufen
@ Roon13 überprüfen Sie diese Frage stackoverflow.com/questions/5528288/…
mikebertiean
@mikebertiean Wie kann ich den startForeground von MainActivity aus aufrufen? Wie kann ich die Benachrichtigung von MainActvity löschen, wenn der Prozess abgeschlossen ist?
Roon13
@mikebertiean Ich habe festgestellt, dass ich startForeground in der Serviceklasse erneut aufrufen muss, aber wie? Muss ich startService () erneut aufrufen?
Roon13
30

Dies ist mein Code, um den Dienst in den Vordergrund zu stellen:

private void runAsForeground(){
    Intent notificationIntent = new Intent(this, RecorderMainActivity.class);
    PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,
            notificationIntent, Intent.FLAG_ACTIVITY_NEW_TASK);

    Notification notification=new NotificationCompat.Builder(this)
                                .setSmallIcon(R.drawable.ic_launcher)
                                .setContentText(getString(R.string.isRecording))
                                .setContentIntent(pendingIntent).build();

    startForeground(NOTIFICATION_ID, notification);

}

Ich muss eine Benachrichtigung mit PendingIntent erstellen, damit ich meine Hauptaktivität von der Benachrichtigung aus starten kann.

Um die Benachrichtigung zu entfernen, rufen Sie einfach stopForeground (true) auf.

Es wird im onStartCommand () aufgerufen. Bitte beziehen Sie sich auf meinen Code unter: https://github.com/bearstand/greyparrot/blob/master/src/com/xiong/richard/greyparrot/Mp3Recorder.java

Richard
quelle
Wenn Sie die Benachrichtigung entfernen, die stopForeground (true) aufruft, brechen Sie den Startforeground-Dienst ab
sdelvalle57
6
Woher nennst du diese Methode?
Srujan Barai
7
Intent.FLAG_ACTIVITY_NEW_TASKist nicht gültig im Kontext von PendingIntent.
Mixel
30

Lösung für Oreo 8.1

Ich habe einige Probleme wie RemoteServiceException aufgrund einer ungültigen Kanal-ID mit den neuesten Versionen von Android festgestellt . So habe ich es gelöst:

Aktivität :

override fun onCreate(savedInstanceState: Bundle?) {
    val intent = Intent(this, BackgroundService::class.java)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(intent)
    } else {
        startService(intent)
    }
}

BackgroundService:

override fun onCreate() {
    super.onCreate()
    startForeground()
}

private fun startForeground() {

    val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    val channelId =
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                createNotificationChannel()
            } else {
                // If earlier version channel ID is not used
                // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
                ""
            }

    val notificationBuilder = NotificationCompat.Builder(this, channelId )
    val notification = notificationBuilder.setOngoing(true)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setPriority(PRIORITY_MIN)
            .setCategory(Notification.CATEGORY_SERVICE)
            .build()
    startForeground(101, notification)
}


@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(): String{
    val channelId = "my_service"
    val channelName = "My Background Service"
    val chan = NotificationChannel(channelId,
            channelName, NotificationManager.IMPORTANCE_HIGH)
    chan.lightColor = Color.BLUE
    chan.importance = NotificationManager.IMPORTANCE_NONE
    chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
    val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    service.createNotificationChannel(chan)
    return channelId
}

JAVA ÄQUIVALENT

public class YourService extends Service {

    // Constants
    private static final int ID_SERVICE = 101;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

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

        // do stuff like register for BroadcastReceiver, etc.

        // Create the Foreground Service
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? createNotificationChannel(notificationManager) : "";
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
        Notification notification = notificationBuilder.setOngoing(true)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setPriority(PRIORITY_MIN)
                .setCategory(NotificationCompat.CATEGORY_SERVICE)
                .build();

        startForeground(ID_SERVICE, notification);
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private String createNotificationChannel(NotificationManager notificationManager){
        String channelId = "my_service_channelid";
        String channelName = "My Foreground Service";
        NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
        // omitted the LED color
        channel.setImportance(NotificationManager.IMPORTANCE_NONE);
        channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        notificationManager.createNotificationChannel(channel);
        return channelId;
    }
}
Rawa
quelle
8
Sie können ContextCompat.startForegroundService(Context,Intent)in Ihrer Aktivität verwenden, was das Richtige tut. ( developer.android.com/reference/android/support/v4/content/… )
Simon Featherstone
3
Sie werden wahrscheinlich verwenden wollen, .setCategory(NotificationCompat.CATEGORY_SERVICE)anstatt, Notification.CATEGORY_SERVICEwenn Ihre min API <21
Jemand irgendwo
6
Bitte beachten Sie, dass das Targeting von Apps Build.VERSION_CODES.P(API-Stufe 28) oder höher die Erlaubnis anfordern muss Manifest.permission.FOREGROUND_SERVICE, um verwendet zu werden startForeground()- siehe developer.android.com/reference/android/app/…
Vadim Kotov
21

Zusätzlich zur RAWA- Antwort bietet dieser Code-Frieden:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(intent)
} else {
    startService(intent)
}

Sie können ändern zu:

ContextCompat.startForegroundService(context, yourIntent);

Wenn Sie sich diese Methode ansehen, können Sie sehen, dass diese Methode alle Überprüfungsarbeiten für Sie erledigt.

Edgar Khimich
quelle
9

Wenn Sie IntentService zu einem Vordergrunddienst machen möchten

dann sollten Sie außer Kraft setzen , onHandleIntent()wie dies

Override
protected void onHandleIntent(@Nullable Intent intent) {


    startForeground(FOREGROUND_ID,getNotification());     //<-- Makes Foreground

   // Do something

    stopForeground(true);                                // <-- Makes it again a normal Service                         

}

Wie mache ich eine Benachrichtigung?

einfach. Hier ist die getNotification()Methode

public Notification getNotification()
{

    Intent intent = new Intent(this, SecondActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);


    NotificationCompat.Builder foregroundNotification = new NotificationCompat.Builder(this);
    foregroundNotification.setOngoing(true);

    foregroundNotification.setContentTitle("MY Foreground Notification")
            .setContentText("This is the first foreground notification Peace")
            .setSmallIcon(android.R.drawable.ic_btn_speak_now)
            .setContentIntent(pendingIntent);


    return foregroundNotification.build();
}

Tieferes Verständnis

Was passiert, wenn ein Dienst zu einem Vordergrunddienst wird?

Das passiert

Geben Sie hier die Bildbeschreibung ein

Was ist ein Vordergrunddienst?

Ein Vordergrunddienst,

  • stellt sicher, dass der Benutzer aktiv weiß, dass im Hintergrund etwas passiert, indem er die Benachrichtigung bereitstellt.

  • (am wichtigsten) wird vom System nicht beendet, wenn der Arbeitsspeicher knapp wird

Ein Anwendungsfall für den Vordergrunddienst

Implementieren der Song-Download-Funktion in einer Musik-App

Rohit Singh
quelle
5

Fügen Sie die angegebene Code-Serviceklasse für "OS> = Build.VERSION_CODES.O" in onCreate () hinzu.

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

     .................................
     .................................

    //For creating the Foreground Service
    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : "";
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, channelId);
    Notification notification = notificationBuilder.setOngoing(true)
            .setSmallIcon(R.mipmap.ic_launcher)
           // .setPriority(PRIORITY_MIN)
            .setCategory(NotificationCompat.CATEGORY_SERVICE)
            .build();

    startForeground(110, notification);
}



@RequiresApi(Build.VERSION_CODES.O)
private String getNotificationChannel(NotificationManager notificationManager){
    String channelId = "channelid";
    String channelName = getResources().getString(R.string.app_name);
    NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
    channel.setImportance(NotificationManager.IMPORTANCE_NONE);
    channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
    notificationManager.createNotificationChannel(channel);
    return channelId;
}

Fügen Sie diese Berechtigung in die Manifestdatei ein:

 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Kush
quelle
1

Behandeln Sie die Absicht auf startCommand of Service mithilfe von.

 stopForeground(true)

Durch diesen Aufruf wird der Dienst aus dem Vordergrundstatus entfernt , sodass er beendet werden kann, wenn mehr Speicher benötigt wird. Dies verhindert nicht, dass der Dienst ausgeführt wird. Dazu müssen Sie stopSelf () oder verwandte Methoden aufrufen .

Wenn Sie den Wert true oder false übergeben, wird angezeigt, ob Sie die Benachrichtigung entfernen möchten oder nicht.

val ACTION_STOP_SERVICE = "stop_service"
val NOTIFICATION_ID_SERVICE = 1
...  
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    super.onStartCommand(intent, flags, startId)
    if (ACTION_STOP_SERVICE == intent.action) {
        stopForeground(true)
        stopSelf()
    } else {
        //Start your task

        //Send forground notification that a service will run in background.
        sendServiceNotification(this)
    }
    return Service.START_NOT_STICKY
}

Erledige deine Aufgabe, wenn bei destroy von stopSelf () aufgerufen wird .

override fun onDestroy() {
    super.onDestroy()
    //Stop whatever you started
}

Erstellen Sie eine Benachrichtigung, um den Dienst im Vordergrund zu halten.

//This is from Util class so as not to cloud your service
fun sendServiceNotification(myService: Service) {
    val notificationTitle = "Service running"
    val notificationContent = "<My app> is using <service name> "
    val actionButtonText = "Stop"
    //Check android version and create channel for Android O and above
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        //You can do this on your own
        //createNotificationChannel(CHANNEL_ID_SERVICE)
    }
    //Build notification
    val notificationBuilder = NotificationCompat.Builder(applicationContext, CHANNEL_ID_SERVICE)
    notificationBuilder.setAutoCancel(true)
            .setDefaults(NotificationCompat.DEFAULT_ALL)
            .setWhen(System.currentTimeMillis())
            .setSmallIcon(R.drawable.ic_location)
            .setContentTitle(notificationTitle)
            .setContentText(notificationContent)
            .setVibrate(null)
    //Add stop button on notification
    val pStopSelf = createStopButtonIntent(myService)
    notificationBuilder.addAction(R.drawable.ic_location, actionButtonText, pStopSelf)
    //Build notification
    val notificationManagerCompact = NotificationManagerCompat.from(applicationContext)
    notificationManagerCompact.notify(NOTIFICATION_ID_SERVICE, notificationBuilder.build())
    val notification = notificationBuilder.build()
    //Start notification in foreground to let user know which service is running.
    myService.startForeground(NOTIFICATION_ID_SERVICE, notification)
    //Send notification
    notificationManagerCompact.notify(NOTIFICATION_ID_SERVICE, notification)
}

Geben Sie bei der Benachrichtigung eine Stopp-Schaltfläche ein, um den Dienst zu beenden, wenn der Benutzer dies benötigt.

/**
 * Function to create stop button intent to stop the service.
 */
private fun createStopButtonIntent(myService: Service): PendingIntent? {
    val stopSelf = Intent(applicationContext, MyService::class.java)
    stopSelf.action = ACTION_STOP_SERVICE
    return PendingIntent.getService(myService, 0,
            stopSelf, PendingIntent.FLAG_CANCEL_CURRENT)
}
Rana Ranvijay Singh
quelle
1

Hinweis: Wenn Ihre App auf API-Level 26 oder höher abzielt, legt das System Einschränkungen für die Verwendung oder Erstellung von Hintergrunddiensten fest, es sei denn, die App selbst steht im Vordergrund.

Wenn eine App einen Vordergrunddienst erstellen muss, sollte die App aufrufen startForegroundService(). Diese Methode erstellt einen Hintergrunddienst, aber die Methode signalisiert dem System, dass der Dienst sich selbst in den Vordergrund stellt.

Sobald der Dienst erstellt wurde, muss der Dienst seine aufrufen startForeground() method within five seconds.

Andrii Kovalchuk
quelle
1
Ich hoffe, Sie sprechen über die aktuelle Frage. Andernfalls gibt es in der Stackoverflow-Community keine solche Regel
Farid
@RogerGusmao im produktionsbereiten Umgebungscode speichert Ihr Projekt nicht immer. Außerdem gibt es viele großartige Beispiele mit Code unterhalb und oberhalb meiner Antwort. Mein Projekt hatte während der Veröffentlichung Probleme, genau weil ich nichts davon wusstestartForegroundService Methode
Andrii Kovalchuk
0

In meinem Fall war es völlig anders, da ich keine Aktivität hatte, um den Dienst in Oreo zu starten.

Im Folgenden sind die Schritte aufgeführt, mit denen ich dieses Problem mit dem Vordergrunddienst behoben habe:

public class SocketService extends Service {
    private String TAG = this.getClass().getSimpleName();

    @Override
    public void onCreate() {
        Log.d(TAG, "Inside onCreate() API");
        if (Build.VERSION.SDK_INT >= 26) {
            NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
            mBuilder.setSmallIcon(R.drawable.ic_launcher);
            mBuilder.setContentTitle("Notification Alert, Click Me!");
            mBuilder.setContentText("Hi, This is Android Notification Detail!");
            NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

            // notificationID allows you to update the notification later on.
            mNotificationManager.notify(100, mBuilder.build());
            startForeground(100, mBuilder.mNotification);
        }
        Toast.makeText(getApplicationContext(), "inside onCreate()", Toast.LENGTH_LONG).show();
    }


    @Override
    public int onStartCommand(Intent resultIntent, int resultCode, int startId) {
        Log.d(TAG, "inside onStartCommand() API");

        return startId;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "inside onDestroy() API");

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
}

Und danach, um diesen Dienst zu initiieren, habe ich unter cmd ausgelöst -


Die Shell adb -s "+ serial_id +" ist startforegroundservice -n com.test.socket.sample / .SocketService


Dies hilft mir also, den Dienst ohne Aktivität auf Oreo-Geräten zu starten :)

Arpana
quelle
0

@mikebertiean Lösung hat fast den Trick getan, aber ich hatte dieses Problem mit zusätzlichen Twist - ich verwende Gingerbread-System und ich wollte kein zusätzliches Paket hinzufügen, nur um die Benachrichtigung auszuführen. Endlich fand ich: https://android.googlesource.com/platform/frameworks/support.git+/f9fd97499795cd47473f0344e00db9c9837eea36/v4/gingerbread/android/support/v4/app/NotificationCompatGingerbread.java

dann stoße ich auf ein zusätzliches Problem - Benachrichtigung beendet einfach meine App, wenn sie ausgeführt wird (wie man dieses Problem löst: Android: So vermeidet man, dass das Klicken auf eine Benachrichtigung onCreate () aufruft ), sodass mein Code im Dienst insgesamt so aussieht (C # / Xamarin):

Intent notificationIntent = new Intent(this, typeof(MainActivity));
// make the changes to manifest as well
notificationIntent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification(Resource.Drawable.Icon, "Starting service");
notification.SetLatestEventInfo(this, "MyApp", "Monitoring...", pendingIntent);
StartForeground(1337, notification);
Greenoldman
quelle