Der Android-Dienst wird beendet, wenn die App geschlossen wird

76

Ich starte einen Dienst von meiner Hauptaktivität für Android aus wie folgt:

final Context context = base.getApplicationContext();
final Intent intent = new Intent(context, MyService.class);
startService(intent);

Wenn ich die Aktivitätsseite schließe, indem ich sie aus der Liste der zuletzt verwendeten Apps herauswische, wird der Dienst nicht mehr ausgeführt und nach einiger Zeit neu gestartet. Ich kann aufgrund meiner App-Anforderungen keine dauerhaften Dienste mit Benachrichtigungen verwenden. Wie kann ich den Dienst NICHT neu starten oder herunterfahren lassen und beim Beenden der App einfach weiterlaufen lassen?

Bam
quelle
1
Mögliches Duplikat von Der Prozess des Dienstes wird
Kiran
@ kiran Das Problem, mit dem ich konfrontiert bin, ist, dass der Dienst neu gestartet wird, wenn die Aktivität geschlossen wird. Ich suche nach einer Möglichkeit, den Dienst jederzeit am Laufen zu halten (ohne einen Neustart nach Beendigung der Aktivität).
Bam
1
Ich denke, wir können das nicht erreichen. Bei geringen Ressourcen wird Ihr Dienst beendet. Das Beste, was wir hoffen können, ist ein Neustart. Und ab 4.4 startet Swipe Kill den Dienst nicht einmal neu. Bitte lesen Sie diesen Thread
Kiran
Überlegen Sie genau, ob Ihr Dienst wirklich immer buchstäblich ausgeführt werden muss: Dies ist schlecht für den Strom- und Speicherverbrauch.
Poolie

Antworten:

48

Ich bin in der gleichen Situation, bis jetzt habe ich erfahren, dass der Dienst geschlossen wird, wenn die App geschlossen wird, auch weil sie sich in einem Thread befinden. Der Dienst sollte sich also in einem anderen Thread befinden, damit er nicht geschlossen wird und versuchen Sie, den Dienst mit dem Alarmmanager am Leben zu erhalten. Hier ein Beispiel: http://www.vogella.com/articles/AndroidServices/article.html Auf diese Weise wird Ihr Dienst nicht in der Benachrichtigung angezeigt.

Nach all den Recherchen, die ich durchgeführt habe, stelle ich schließlich fest, dass die beste Wahl für einen lang laufenden Dienst ist startForeground(), weil er dafür gemacht ist und das System tatsächlich gut mit Ihrem Dienst umgeht.

aljuaid86
quelle
10
@Bam Ich habe genau dieses Problem. Mein Dienst ist jedoch ein Vordergrunddienst und wird immer noch beendet, wenn meine Aktivität geschlossen wird. Ich habe mein Argument für die Absichtsinitialisierung vom Kontext getApplicationContext () in getBaseContext () geändert und das Problem behoben.
JayB
1
Kann ich getBaseContext () in pendingIntent verwenden?
Kairi San
1
anstatt zu verwenden, notificationManager.notify(intent.getIntExtra("notification_Id", 0), builder.build());sollte ich verwenden startForeground(intent.getIntExtra("notification_Id, 0), builder.build());? Ich benutze NotificationCompat.Builder
Kairi San
startForeground () brauche API 26 Gibt es eine Lösung?
Midhilaj
13

Lassen Sie sich in Ihrem Mainifest so bedienen

 <service
            android:name=".sys.service.youservice"
            android:exported="true"
        android:process=":ServiceProcess" />

Dann wird Ihr Dienst auf einem anderen Prozess namens ServiceProcess ausgeführt


Wenn du willst, dass dein Dienst niemals stirbt:

  1. onStartCommand () gibt START_STICKY zurück

  2. onDestroy () -> startself

  3. Erstellen Sie einen Deamon-Service

  4. jin -> Erstelle einen Native Deamon-Prozess. Auf github findest du einige Open-Source-Projekte

  5. startForeground () gibt es eine Möglichkeit ,Foreground ohne Benachrichtigung zu starten, googeln Sie es

Alanban
quelle
12
Ziemlich sicher, dass man mit onDestroy anfängt, wird als Kampf gegen das Android-System angesehen, was eine schlechte Praxis ist.
Flare Cat
12

Dies kann Ihnen helfen. Ich kann mich irren, aber es scheint mir, dass dies mit der Rückkehr START_STICKYin Ihre onStartCommand()Methode zusammenhängt. Sie können verhindern, dass der Dienst erneut aufgerufen wird, indem Sie START_NOT_STICKYstattdessen zurückkehren .

Artex
quelle
4
Tatsächlich wird der Dienst nicht mehr ausgeführt, wenn ich die App beende oder schließe (und dann den Dienst neu startet). Ich möchte nicht, dass der Dienst angehalten / gestoppt wird, wenn ich die App beende / schließe. Ich möchte, dass es weiterläuft. Ich glaube, die Lösung hat etwas damit zu tun, den Dienst in einem separaten Prozess auszuführen, bin mir aber nicht sicher.
Bam
Working Fine Artex, danke
Manikandan K
7

Dienstleistungen sind manchmal ziemlich kompliziert.

Wenn Sie einen Dienst von einer Aktivität (oder Ihrem Prozess) aus starten, befindet sich der Dienst im Wesentlichen im selben Prozess.

Zitieren aus den Entwicklernotizen

Die meiste Verwirrung über die Service-Klasse dreht sich tatsächlich um das, was es nicht ist:

Ein Service ist kein separater Prozess. Das Service-Objekt selbst bedeutet nicht, dass es in einem eigenen Prozess ausgeführt wird. Sofern nicht anders angegeben, wird es im selben Prozess ausgeführt wie die Anwendung, zu der es gehört.

Ein Service ist kein Thread. Es ist kein Mittel, die Arbeit vom Haupt-Thread aus zu erledigen (um Fehler zu vermeiden, die auf die Anwendung nicht reagieren).

Dies bedeutet also, dass der Benutzer Ihren Prozess löscht, wenn der Benutzer die App von den letzten Aufgaben entfernt (dies schließt alle Ihre Aktivitäten usw. ein). Nehmen wir nun drei Szenarien.

Erstens, wenn der Dienst keine Vordergrundbenachrichtigung hat.

In diesem Fall wird Ihr Prozess zusammen mit Ihrem Dienst beendet.

Zweitens , wo der Dienst hat eine Vordergrund Benachrichtigung

In diesem Fall wird der Dienst nicht beendet und der Prozess auch nicht

Drittes Szenario Wenn der Dienst keine Vordergrundbenachrichtigung hat, kann er weiterhin ausgeführt werden, wenn die App geschlossen wird. Wir können dies tun, indem wir den Service in einem anderen Prozess ausführen. (Ich habe jedoch einige Leute sagen hören, dass es möglicherweise nicht funktioniert. Es bleibt Ihnen überlassen, es selbst auszuprobieren. )

Sie können einen Service in einem separaten Prozess erstellen, indem Sie das folgende Attribut in Ihr Manifest aufnehmen.

android: process = ": yourService"

oder

android: process = "yourService" Prozessname muss mit Kleinbuchstaben beginnen.

Zitieren aus Entwicklernotizen

Wenn der diesem Attribut zugewiesene Name mit einem Doppelpunkt (':') beginnt, wird bei Bedarf ein neuer Prozess erstellt, der für die Anwendung privat ist, und der Dienst wird in diesem Prozess ausgeführt. Wenn der Prozessname mit einem Kleinbuchstaben beginnt , wird der Dienst in einem globalen Prozess mit diesem Namen ausgeführt, sofern er dazu berechtigt ist. Auf diese Weise können Komponenten in verschiedenen Anwendungen einen Prozess gemeinsam nutzen, wodurch der Ressourcenverbrauch verringert wird.

das habe ich gesammelt, wenn jemand ein Experte ist, korrigiere mich bitte, wenn ich falsch liege :)

mmmcho
quelle
4

Das Hauptproblem besteht darin, dass der Dienst nicht gestartet werden kann, wenn die App geschlossen wird. Das Android-Betriebssystem ( in einigen Betriebssystemen ) beendet den Dienst für die Ressourcenoptimierung. Wenn Sie den Dienst nicht neu starten können, rufen Sie einen Alarmmanager an, um den Empfänger wie folgt zu starten. Hier ist der gesamte Code. Dieser Code wird Ihren Dienst am Leben erhalten.

Manifest ist,

         <service
            android:name=".BackgroundService"
            android:description="@string/app_name"
            android:enabled="true"
            android:label="Notification" />
        <receiver android:name="AlarmReceiver">
            <intent-filter>
                <action android:name="REFRESH_THIS" />
            </intent-filter>
        </receiver>

IN Main Activty Start Alarm Manager auf diese Weise,

String alarm = Context.ALARM_SERVICE;
        AlarmManager am = (AlarmManager) getSystemService(alarm);

        Intent intent = new Intent("REFRESH_THIS");
        PendingIntent pi = PendingIntent.getBroadcast(this, 123456789, intent, 0);

        int type = AlarmManager.RTC_WAKEUP;
        long interval = 1000 * 50;

        am.setInexactRepeating(type, System.currentTimeMillis(), interval, pi);

Dies wird Empfänger nennen und Empfänger ist,

public class AlarmReceiver extends BroadcastReceiver {
    Context context;

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;

        System.out.println("Alarma Reciver Called");

        if (isMyServiceRunning(this.context, BackgroundService.class)) {
            System.out.println("alredy running no need to start again");
        } else {
            Intent background = new Intent(context, BackgroundService.class);
            context.startService(background);
        }
    }

    public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);

        if (services != null) {
            for (int i = 0; i < services.size(); i++) {
                if ((serviceClass.getName()).equals(services.get(i).service.getClassName()) && services.get(i).pid != 0) {
                    return true;
                }
            }
        }
        return false;
    }
}

Und dieser Alaram-Empfänger ruft einmal an, wenn die Android-App geöffnet und die App geschlossen wird.

public class BackgroundService extends Service {
    private String LOG_TAG = null;

    @Override
    public void onCreate() {
        super.onCreate();
        LOG_TAG = "app_name";
        Log.i(LOG_TAG, "service created");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(LOG_TAG, "In onStartCommand");
        //ur actual code
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Wont be called as service is not bound
        Log.i(LOG_TAG, "In onBind");
        return null;
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Log.i(LOG_TAG, "In onTaskRemoved");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(LOG_TAG, "In onDestroyed");
    }
}
Ajith KP
quelle
2

Versuchen Sie dies, der Dienst wird weiterhin im Hintergrund ausgeführt.

BackServices.class

public class BackServices extends Service{

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

    @Override
   public int onStartCommand(Intent intent, int flags, int startId) {
      // Let it continue running until it is stopped.
      Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
      return START_STICKY;
   }
   @Override
   public void onDestroy() {
      super.onDestroy();
      Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
   }
}

Lassen Sie in Ihrer MainActivityonCreate diese Codezeile fallen

startService(new Intent(getBaseContext(), BackServices.class));

Jetzt läuft der Dienst im Hintergrund weiter.

Youssef Al Subaihi
quelle
7
Das Problem ist immer noch da: Wenn ich die App über das Menü "Letzte" beende, wird der Dienst neu gestartet (onStartCommand wird erneut aufgerufen). Das einzige, was mir geholfen hat, war, es zu einem Vordergrunddienst zu machen.
Bam
1

Die Verwendung des gleichen Prozesses für den Dienst und die Aktivität und START_STICKY oder START_REDELIVER_INTENT im Dienst ist die einzige Möglichkeit, den Dienst beim Neustart der Anwendung neu zu starten. Dies geschieht beispielsweise, wenn der Benutzer die Anwendung schließt, aber auch, wenn das System entscheidet um es aus Optimierungsgründen zu schließen. Sie können keinen Dienst haben, der ohne Unterbrechung dauerhaft ausgeführt wird. Dies ist beabsichtigt, Smartphones sind nicht dafür ausgelegt, über einen längeren Zeitraum kontinuierliche Prozesse auszuführen. Dies liegt an der Tatsache, dass die Batterielebensdauer die höchste Priorität hat. Sie müssen Ihren Service so gestalten, dass er jederzeit gestoppt werden kann.

stef
quelle
1

Sie müssen diesen Code zu Ihrer Service-Klasse hinzufügen, damit er den Fall behandelt, in dem Ihr Prozess beendet wird

 @Override
    public void onTaskRemoved(Intent rootIntent) {
        Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
        restartServiceIntent.setPackage(getPackageName());

        PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
        AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        alarmService.set(
                AlarmManager.ELAPSED_REALTIME,
                SystemClock.elapsedRealtime() + 1000,
                restartServicePendingIntent);

        super.onTaskRemoved(rootIntent);
    }
Kaustubh Bhagwat
quelle
0

Warum nicht einen IntentService nutzen?

IntentService öffnet einen neuen Thread neben dem Haupt-Thread und arbeitet dort. Auf diese Weise wird das Schließen der App nicht beeinflusst

Beachten Sie, dass IntentService onHandleIntent () ausführt. Wenn der Dienst abgeschlossen ist, prüfen Sie, ob er Ihren Anforderungen entspricht. http://developer.android.com/reference/android/app/IntentService.html

Sharon Gur
quelle
2
Eigentlich war meine Anforderung, einen immer laufenden Hintergrunddienst zu haben. Das Starten des Dienstes als Vordergrunddienst hat es für mich gelöst.
Bam
0

Die beste Lösung ist, den Synchronisierungsadapter in Android zu verwenden, um den Dienst zu starten. Erstellen Sie einen Synchronisierungsadapter und rufen Sie den Startdienst ihrer .. on onPerformSync-Methode auf. Informationen zum Erstellen eines Synchronisierungskontos finden Sie unter folgendem Link: https://developer.android.com/training/sync-adapters/index.html

Warum SyncAdapter? Antwort: Weil Sie den Dienst früher über Ihren App-Kontext gestartet haben. Wenn Ihr App-Prozess zu diesem Zeitpunkt beendet wird (wenn Sie ihn aus Task-Manager oder Betriebssystem entfernen, weil er nicht über ausreichende Ressourcen verfügt), wird zu diesem Zeitpunkt auch Ihr Dienst entfernt. SyncAdapter funktioniert nicht im Anwendungsthread. Wenn Sie also darin aufrufen, wird der Dienst nicht mehr entfernt. Es sei denn, Sie schreiben Code, um ihn zu entfernen.

Nishanth S Babu
quelle
0
<service android:name=".Service2"
            android:process="@string/app_name"
            android:exported="true"
            android:isolatedProcess="true"
            />

Erklären Sie dies in Ihrem Manifest. Geben Sie Ihrem Prozess einen benutzerdefinierten Namen und machen Sie diesen Prozess isoliert und exportiert.

Nishanth S Babu
quelle
0

Das Ausführen eines Intent-Dienstes ist einfacher. Dienst beim Erstellen eines Threads in der Anwendung, der sich jedoch noch in der Anwendung befindet.

Seb
quelle
-5

Überschreiben Sie einfach die onDestroy-Methode in Ihrer ersten sichtbaren Aktivität, z. B. nach dem Splash haben Sie eine Homepage, und während Sie von Splash zu Homepage umleiten, haben Sie Splash bereits beendet. Also auf der Startseite zerstören. und beenden Sie den Dienst in dieser Methode.

Urvish Shah
quelle