Die App läuft weiter, wenn der Vordergrunddienst zuletzt gestoppt wurde

9

Ich bin auf ein Verhalten im Prozessmanagement von Android in Verbindung mit Vordergrunddiensten gestoßen, das mich wirklich verwirrt.

Was ist für mich vernünftig

  1. Wenn Sie Ihre App von "Letzte Apps" streichen, sollte das Betriebssystem den App-Prozess in relativ naher Zukunft beenden.
  2. Wenn Sie Ihre App von "Letzte Apps" wischen, während Sie einen Vordergrunddienst ausführen, bleibt die App am Leben.
  3. Wenn Sie den Vordergrunddienst beenden, bevor Sie Ihre App von "Letzte Apps" wischen, erhalten Sie dasselbe wie für 1).

Was mich verwirrt

Wenn Sie den Vordergrunddienst beenden, während keine Aktivitäten im Vordergrund sind (App wird NICHT in "Letzte Apps" angezeigt), würde ich erwarten, dass die App jetzt beendet wird.

Dies geschieht jedoch nicht, der App-Prozess ist noch am Leben.

Beispiel

Ich habe ein minimales Beispiel erstellt, das dieses Verhalten zeigt.

Der ForegroundService:

import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import timber.log.Timber

class MyService : Service() {

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onCreate() {
        super.onCreate()
        Timber.d("onCreate")
    }

    override fun onDestroy() {
        super.onDestroy()
        Timber.d("onDestroy")

        // just to make sure the service really stops
        stopForeground(true)
        stopSelf()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Timber.d("onStartCommand")
        startForeground(ID, serviceNotification())
        return START_NOT_STICKY
    }

    private fun serviceNotification(): Notification {
        createChannel()

        val stopServiceIntent = PendingIntent.getBroadcast(
            this,
            0,
            Intent(this, StopServiceReceiver::class.java),
            PendingIntent.FLAG_UPDATE_CURRENT
        )
        return NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("This is my service")
            .setContentText("It runs as a foreground service.")
            .addAction(0, "Stop", stopServiceIntent)
            .build()
    }

    private fun createChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationManager = getSystemService(NotificationManager::class.java)
            notificationManager.createNotificationChannel(
                NotificationChannel(
                    CHANNEL_ID,
                    "Test channel",
                    NotificationManager.IMPORTANCE_DEFAULT
                )
            )
        }
    }

    companion object {
        private const val ID = 532207
        private const val CHANNEL_ID = "test_channel"

        fun newIntent(context: Context) = Intent(context, MyService::class.java)
    }
}

Der BroadcastReceiver, um den Dienst zu beenden:

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent

class StopServiceReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {

        val serviceIntent = MyService.newIntent(context)

        context.stopService(serviceIntent)
    }
}

Die Aktivität:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        startService(MyService.newIntent(this))
    }
}

Das Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.christophlutz.processlifecycletest">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <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=".MyService"/>
        <receiver android:name=".StopServiceReceiver" />
    </application>

</manifest>

Probieren Sie es folgendermaßen aus:

  1. App starten, Vordergrunddienst beenden, App aus 'Letzte Apps' entfernen
  2. App starten, App aus 'Letzte Apps' entfernen, Vordergrunddienst beenden

Sie können in LogCat von Android Studio sehen, dass der App-Prozess für Fall 1 mit [DEAD] markiert ist, für Fall 2 jedoch nicht.

Da es ziemlich einfach zu reproduzieren ist, könnte es sich um ein beabsichtigtes Verhalten handeln, aber ich habe in den Dokumenten keine wirkliche Erwähnung gefunden.

Weiß jemand, was hier los ist?

Christoph Lutz
quelle

Antworten:

0

Dies hängt davon ab, was der Vordergrunddienst tatsächlich tut. Wenn Threads, Netzwerkverbindungen, Datei-E / A usw. verwendet werden, die aktiv Speicher verbrauchen, wird der Dienst nicht zerstört, auch wenn Sie versuchen, den Dienst zu beenden, sodass der Prozess am Leben bleibt. Dies schließt auch alle Rückrufe in der Schnittstelle ein, die am Leben bleiben, während Sie versuchen, den Dienst zu beenden. Insbesondere Threads, die noch ausgeführt (sogar unterbrochen) und gebundene Dienste blockieren, blockieren den Lebenszyklus, in dem der Dienst ordnungsgemäß gestoppt wird.

Stellen Sie sicher, dass Ihr Dienst keine Speicherlecks aufweist, und schließen Sie jede Verbindung (Datenbank, Netzwerk usw.). Entfernen Sie alle Rückrufe von allen Schnittstellen, bevor Sie den Dienst beenden. Sie können sicherstellen, dass der Dienst zerstört wird, wenn er onDestroy()aufgerufen wird.

Für verschiedene Prozesse: Ich glaube, der Grund dafür, dass der Prozess am Leben bleibt, ist, dass das System sieht, dass der Dienst für einige Zeit erneut gestartet werden kann, sodass der Prozess für kurze Zeit am Leben bleibt. Zumindest habe ich das beobachtet, denn selbst nachdem onDestroy()der Prozess aufgerufen wurde, blieb er am Leben, ich konnte ihn vom Debugger aus sehen.

Wenn Sie sicherstellen möchten (auch wenn dies nicht empfohlen wird), dass der Prozess sicher beendet wird, nachdem alles zerstört wurde, können Sie dies jederzeit aufrufen, um den Prozess selbst zu beenden:

android.os.Process.killProcess(android.os.Process.myPid());
Furkan Yurdakul
quelle
Sie können das Verhalten anhand des Beispiels aus der Frage "Keine Verbindungen, Threads oder andere Elemente werden weiterhin ausgeführt" reproduzieren. onDestroy(Service) wird aufgerufen, aber der Prozess bleibt lange am Leben, nachdem alles weg ist. Selbst wenn das Betriebssystem den Prozess für den Fall, dass der Dienst neu gestartet wird, am Leben erhalten hat, verstehe ich nicht, warum es nicht dasselbe tut, wenn Sie den Dienst zuerst beenden und dann die App aus den letzten Jahren entfernen. Das angezeigte Verhalten scheint eher unintuitiv zu sein, insbesondere angesichts der jüngsten Änderungen der Ausführungsbeschränkungen im Hintergrund.
Daher
@DavidMedenjak Versuchen getApplicationContext().startService(MyService.newIntent(this));Sie es anstelle von dem, was Sie verwenden, und teilen Sie mir bitte die Ergebnisse mit. Ich glaube, die Aktivität bleibt am Leben, weil der Aktivitätskontext anstelle des Anwendungskontexts verwendet wird. Auch wenn Sie auf Oreo oder höher testen, versuchen Sie es mitgetApplicationContext().startforegroundService(MyService.newIntent(this));
Furkan Yurdakul
0

Das Android-System ist bekannt für seine Selbsterkenntnis in Bezug auf Speicher, Prozessorleistung und Lebensdauer der Anwendungsprozesse. Es entscheidet selbst, ob ein Prozess beendet wird oder nicht (dasselbe gilt für Aktivitäten und Dienste).

Hier ist die offizielle Dokumentation zu diesem Thema.

Schauen Sie sich an, was in Bezug auf den Vordergrund steht

Es wird immer nur wenige solcher Prozesse im System geben, und diese werden nur als letztes Mittel beendet, wenn der Speicher so niedrig ist, dass nicht einmal diese Prozesse weiter ausgeführt werden können. Im Allgemeinen hat das Gerät zu diesem Zeitpunkt einen Speicher-Paging-Status erreicht, sodass diese Aktion erforderlich ist, damit die Benutzeroberfläche weiterhin reagiert.

und sichtbare Prozesse (Foreground Service ist ein sichtbarer Prozess)

Die Anzahl dieser Prozesse, die im System ausgeführt werden, ist weniger begrenzt als bei Vordergrundprozessen, aber immer noch relativ gesteuert. Diese Prozesse werden als äußerst wichtig angesehen und werden nur beendet, wenn dies erforderlich ist, um alle Vordergrundprozesse am Laufen zu halten.

Dies bedeutet, dass auf dem Android-Betriebssystem Ihr App-Prozess ausgeführt wird, solange genügend Speicher vorhanden ist, um alle Vordergrundprozesse zu unterstützen. Selbst wenn Sie es stoppen, verschiebt das System es möglicherweise einfach in den Cache Prozesse und behandelt es in einer Warteschlange. Irgendwann wird es so oder so getötet - aber normalerweise liegt es nicht an Ihnen, sich zu entscheiden. Ehrlich gesagt sollte es Ihnen egal sein, was mit Ihrem App-Prozess passiert, nachdem (und solange) alle Android-Lebenszyklusmethoden aufgerufen wurden. Android weiß es besser.

Natürlich können Sie den Prozess mit beenden android.os.Process.killProcess(android.os.Process.myPid()); aber es wird nicht empfohlen, da dies den ordnungsgemäßen Lebenszyklus von Android-Elementen stört und möglicherweise keine ordnungsgemäßen Rückrufe aufgerufen werden. Daher kann sich Ihre App in einigen Fällen schlecht verhalten.

Ich hoffe es hilft.

Pavlo Ostasha
quelle
0

Unter Android entscheidet das Betriebssystem, welche Anwendungen beendet werden.

Es gibt eine Prioritätsreihenfolge:
Anwendungen mit einem Vordergrunddienst werden selten beendet, stattdessen werden andere Apps und Dienste nach einer Zeit der Inaktivität beendet, und genau das passiert mit Ihrer App.

Wenn Sie den Vordergrunddienst beenden, erhöht dies die Wahrscheinlichkeit, dass das System Ihre App beendet. In keinem Fall bedeutet dies jedoch, dass sie sofort beendet wird.

Lluis Felisart
quelle
0

Der Bildschirm " Letzte" [...] ist eine Benutzeroberfläche auf Systemebene, in der kürzlich aufgerufene Aktivitäten und Aufgaben aufgelistet sind .

Sie können mehrere Aktivitäten oder Aufgaben von einer einzigen App in der Liste haben. Es ist nicht eine Recent Apps - Liste.

Daher besteht keine direkte Korrelation zwischen den Elementen des Bildschirms "Letzte" und dem App-Prozess.

Jedenfalls , wenn Sie die letzte Aktivität der App zu schließen und haben nichts anderes im Prozess ausgeführt wird (wie ein Vordergrunddienst), der Prozess wird nur gereinigt.

Wenn Sie also einen laufenden Vordergrund stoppen (durch stopService()oder stopSelf()und Aufheben der Bindung), bereinigt das System auch den Prozess, in dem es ausgeführt wurde.

Es ist also in der Tat ein beabsichtigtes Verhalten .

tynn
quelle