it-swarm.com.de

Android: Läuft der Dienst weiter, wenn die App beendet wird

Ich möchte ein IntentService im Hintergrund laufen lassen, auch wenn die App beendet wird. Und mit "getötet" meine ich drücke lange die Home-Taste -> siehe alle laufenden Apps -> wische meine App beiseite -> App getötet OR Back-Taste lange drücken -> App getötet

Mein Code lautet wie folgt. In meiner Hauptaktivität:

Intent intent = new Intent(this, MyService.class);
this.startService(intent);

In meinem MyService:

public class MyService extends IntentService {

@Override
protected void onHandleIntent(Intent intent) {
    System.out.println("MyService started");
    run();
}

private void run() {
    while (true){
        System.out.println("MyService still running");
        doSomething();
        waitSomeTime();
    }
}

}

Ich sehe, dass der Dienst ausgeführt wird, wenn die App offen ist. Es läuft noch, wenn ich App minimieren per Home-Button. Es läuft immer noch, wenn ich App schließen über den Zurück-Button. Aber es wird aufhören, wenn ich es wie oben erwähnt töte. Wie löse ich das?

37
user2078872

Wenn Ihr Dienst von Ihrer App gestartet wird, wird Ihr Dienst im Hauptprozess ausgeführt. Wenn die App beendet wird, wird der Dienst ebenfalls beendet. Sie können also Broadcasts von der onTaskRemoved Methode Ihres Dienstes wie folgt senden:

 Intent intent = new Intent("com.Android.ServiceStopped");
 sendBroadcast(intent);

und einen Rundfunkempfänger haben, der wieder einen Dienst startet. Ich habe es versucht Der Dienst wird bei allen Arten von Kills neu gestartet.

30
BhendiGawaar

Alle Antworten scheinen richtig zu sein also werde ich hier eine vollständige Antwort geben.

Erstens ist der einfachste Weg, das zu tun, was Sie versuchen, einen Broadcast in Android) zu starten, wenn die - App manuell beendet wird , und definieren Sie ein benutzerdefiniertesBroadcastReceiver, um anschließend einen Neustart des Dienstes auszulösen.

Lassen Sie uns jetzt in den Code springen.


Erstellen Sie Ihren Dienst in _YourService.Java_

Beachten Sie die onCreate() -Methode, bei der wir einen Vordergrunddienst starten anders für Build-Versionen größer als Android Oreo . Dies liegt an den kürzlich eingeführten strengen Benachrichtigungsrichtlinien, in denen wir unseren eigenen Benachrichtigungskanal definieren müssen, um sie korrekt anzuzeigen.

Die this.sendBroadcast(broadcastIntent); in der onDestroy() -Methode ist die Anweisung, die asynchron einen Broadcast mit dem Aktionsnamen _"restartservice"_ sendet. Wir werden dies später als Auslöser verwenden, um unseren Dienst neu zu starten.

Hier haben wir eine einfache Timer-Task definiert, die alle 1 Sekunde im FeldLogeinen Zählerwert ausgibt, während sie sich bei jedem Ausdruck inkrementiert.

_public class YourService extends Service {
public int counter=0;

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
            startMyOwnForeground();
        else
            startForeground(1, new Notification());
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private void startMyOwnForeground()
    {
        String NOTIFICATION_CHANNEL_ID = "example.permanence";
        String channelName = "Background Service";
        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);

        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        assert manager != null;
        manager.createNotificationChannel(chan);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setContentTitle("App is running in background")
                .setPriority(NotificationManager.IMPORTANCE_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        startForeground(2, notification);
    }


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


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

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction("restartservice");
        broadcastIntent.setClass(this, Restarter.class);
        this.sendBroadcast(broadcastIntent);
    }



    private Timer timer;
    private TimerTask timerTask;
    public void startTimer() {
        timer = new Timer();
        timerTask = new TimerTask() {
            public void run() {
                Log.i("Count", "=========  "+ (counter++));
            }
        };
        timer.schedule(timerTask, 1000, 1000); //
    }

    public void stoptimertask() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

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

Erstellen Sie einen Broadcast Receiver, um auf Ihre benutzerdefinierten Broadcasts in _Restarter.Java_ zu antworten

Die Sendung mit dem Aktionsnamen _"restartservice"_ , den Sie gerade in _YourService.Java_ definiert haben, soll nun eine Methode auslösen, die Ihren Dienst neu startet . Dies geschieht mitBroadcastReceiverin Android.

Wir überschreiben die eingebaute onRecieve() -Methode inBroadcastReceiver, um die Anweisung hinzuzufügen, die den Dienst neu startet. Die Funktion startService() wird nicht wie vorgesehen funktionieren ab Android Oreo 8.1, as strenge Hintergrundrichtlinien Beenden Sie den Dienst bald nach dem Neustart, sobald die App beendet ist. Daher verwenden wir die Funktion startForegroundService() für höhere Versionen und zeigen eine fortlaufende Benachrichtigung an, um den Dienst weiter auszuführen.

_public class Restarter extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("Broadcast Listened", "Service tried to stop");
        Toast.makeText(context, "Service restarted", Toast.LENGTH_SHORT).show();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(new Intent(context, YourService.class));
        } else {
            context.startService(new Intent(context, YourService.class));
        }
    }
}
_

Definiere deinen _MainActivity.Java_, um den Dienst beim Start der App aufzurufen.

Hier definieren wir eine separate isMyServiceRunning() Methode, um den aktuellen Status des Hintergrunddienstes zu überprüfen. Wenn der Dienst nicht läuft, starten wir ihn mit startService().

Da die App bereits im Vordergrund ausgeführt wird, müssen wir den Dienst nicht als Vordergrunddienst starten, um zu verhindern, dass er beendet wird.

Beachten Sie, dass wir in onDestroy() speziell stopService() aufrufen, sodass unsere überschriebene Methode aufgerufen wird. Wenn dies nicht getan worden wäre, hätte der Dienst beendet automatisch nachdem die App beendet wurde, ohne unsere modifizierte onDestroy() -Methode in _YourService.Java_ aufzurufen

_public class MainActivity extends AppCompatActivity {
    Intent mServiceIntent;
    private YourService mYourService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mYourService = new YourService();
        mServiceIntent = new Intent(this, mYourService.getClass());
        if (!isMyServiceRunning(mYourService.getClass())) {
            startService(mServiceIntent);
        }
    }

    private boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                Log.i ("Service status", "Running");
                return true;
            }
        }
        Log.i ("Service status", "Not running");
        return false;
    }


    @Override
    protected void onDestroy() {
        stopService(mServiceIntent);
        super.onDestroy();
    }
}
_

Registrieren Sie sie schließlich in Ihrem _AndroidManifest.xml_

Alle oben genannten drei Klassen müssen separat in _AndroidManifest.xml_ registriert werden.

Beachten Sie, dass wir ein _intent-filter_ mit dem Aktionsnamen als _"restartservice"_ definieren, wobei das _Restarter.Java_ als receiver. Dies stellt sicher, dass unser benutzerdefiniertesBroadcastRecieverimmer dann aufgerufen wird, wenn das System auf eine Sendung mit dem angegebenen Aktionsnamen stößt.

_<application
    Android:allowBackup="true"
    Android:icon="@mipmap/ic_launcher"
    Android:label="@string/app_name"
    Android:supportsRtl="true"
    Android:theme="@style/AppTheme">

    <receiver
        Android:name="Restarter"
        Android:enabled="true"
        Android:exported="true">
        <intent-filter>
            <action Android:name="restartservice" />
        </intent-filter>
    </receiver>

    <activity Android:name="MainActivity">
        <intent-filter>
            <action Android:name="Android.intent.action.MAIN" />
            <category Android:name="Android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service
        Android:name="YourService"
        Android:enabled="true" >
    </service>
</application>
_

Dies sollte nun Ihren Dienst erneut starten, wenn die App vom Task-Manager beendet wurde. Dieser Dienst läuft im Hintergrund weiter, solange der Benutzer nicht _Force Stop_ die App aus Anwendungseinstellungen .

UPDATE: Ein dickes Lob an Dr.jacky für den Hinweis. Der oben erwähnte Weg funktioniert nur, wenn die onDestroy() des Dienstes aufgerufen wird, was zu bestimmten Zeiten, die ich nicht kannte, der Fall sein könnte nicht . Vielen Dank.

23
Sayan Sil

Fügen Sie in Ihrem Dienst den folgenden Code hinzu.

@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);
 }
6
Iman Marashi

inside onstart Befehl set START_STICKY... Dieser Dienst wird nicht töten, es sei denn, er erledigt zu viele Aufgaben und der Kernel möchte ihn dafür töten ...

@Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("LocalService", "Received start id " + startId + ": " + intent);
            // We want this service to continue running until it is explicitly
            // stopped, so return sticky.
            return START_STICKY;
        }
5
kiturk3

Der Grund dafür ist, dass Sie versuchen, einen IntentService zu verwenden. Hier ist die Zeile aus dem API Docs

Der IntentService führt folgende Aktionen aus:

Beendet den Dienst, nachdem alle Startanforderungen verarbeitet wurden, sodass Sie stopSelf () niemals aufrufen müssen.

Wenn Sie also möchten, dass Ihr Service auf unbestimmte Zeit ausgeführt wird, sollten Sie stattdessen die Service-Klasse erweitern. Dies garantiert jedoch nicht, dass Ihr Dienst auf unbestimmte Zeit ausgeführt wird. Ihr Dienst hat weiterhin die Möglichkeit, vom Kernel in einem Zustand mit wenig Arbeitsspeicher abgebrochen zu werden, wenn die Priorität niedrig ist. Sie haben also zwei Möglichkeiten:
1) Lassen Sie es im Vordergrund laufen, indem Sie die startForeground() -Methode aufrufen.
2) Starten Sie den Dienst neu, wenn er beendet wird. Hier ist ein Teil des Beispiels aus den Dokumenten, in denen über den Neustart des Dienstes nach dessen Beendigung gesprochen wird

 public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the 
      // start ID so we know which request we're stopping when we finish the job 
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart 
      return START_STICKY;
  }  
4
Rishit Shah

Sie können Android:stopWithTask="false"in manifest as bellow, Dies bedeutet, dass Ihr Dienst auch dann nicht beendet wird, wenn der Benutzer die App durch Entfernen aus der Jobliste beendet.

 <service Android:name=".service.StickyService"
                  Android:stopWithTask="false"/>
1
Melbourne Lopes