L'app continua a funzionare quando il servizio di primo piano viene interrotto per ultimo


9

Mi sono imbattuto in un comportamento nella gestione dei processi di Android in combinazione con i servizi di primo piano, che mi confonde davvero.

Ciò che è ragionevole per me

  1. Quando passi l'app da "App recenti", il sistema operativo dovrebbe terminare il processo dell'app in un futuro relativamente prossimo.
  2. Quando scorri l'app da "App recenti" mentre esegui un servizio in primo piano, l'app rimane attiva.
  3. Quando interrompi il servizio in primo piano prima di scorrere l'app da "App recenti" ottieni lo stesso di 1).

Ciò che mi confonde

Quando interrompi il servizio in primo piano senza avere attività in primo piano (l'app NON viene visualizzata in "App recenti"), mi aspetto che l'app venga uccisa ora.

Tuttavia, ciò non accade, il processo dell'app è ancora attivo.

Esempio

Ho creato un esempio minimo che mostra questo comportamento.

The 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)
    }
}

Il BroadcastReceiver per interrompere il servizio:

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)
    }
}

L'attività:

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))
    }
}

Il 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>

Provalo nei modi seguenti:

  1. Avvia app, interrompi il servizio in primo piano, rimuovi l'app da "App recenti"
  2. Avvia l'app, rimuovi l'app da "App recenti", interrompi il servizio in primo piano

Nel LogCat di Android Studio puoi vedere che il processo dell'app è contrassegnato come [DEAD] per il caso 1 ma non per il caso 2.

Poiché è abbastanza facile riprodurlo, potrebbe essere un comportamento previsto, ma non ho trovato alcuna menzione reale di questo nei documenti.

Qualcuno sa cosa sta succedendo qui?

Risposte:


0

Questo dipende da cosa fa effettivamente il servizio di primo piano. Se utilizza thread, connessioni di rete, I / O di file ecc. Che consumano attivamente memoria, anche se si tenta di interrompere il servizio, non verrà distrutto, quindi il processo rimarrà attivo. Ciò include anche qualsiasi callback di interfaccia che rimanga attivo mentre si sta tentando di interrompere il servizio. Soprattutto i thread ancora in esecuzione (anche interrotti) e i servizi associati bloccano il ciclo di vita che arresta correttamente il servizio.

Assicurarsi di non avere perdite di memoria nel servizio e chiudere tutte le connessioni (database, rete, ecc ...), rimuovere tutti i callback da tutte le interfacce prima di interrompere il servizio. Puoi assicurarti che il servizio verrà distrutto se onDestroy()viene chiamato.

Per diversi processi: credo che la ragione per cui il processo rimane in vita sia che il sistema vede in un modo in cui il servizio potrebbe ricominciare da qualche tempo, quindi mantiene in vita il processo per un breve periodo. Almeno, è quello che ho osservato, perché anche dopo che è onDestroy()stato chiamato, il processo è rimasto in vita, sono stato in grado di vederlo dal debugger.

Se vuoi assicurarti (anche se in questo modo non è raccomandato) che il processo venga definitivamente ucciso dopo che tutto è stato distrutto, puoi sempre chiamarlo per terminare il processo stesso:

android.os.Process.killProcess(android.os.Process.myPid());

Puoi riprodurre il comportamento con l'esempio della domanda & mdash; nessuna connessione, thread o qualsiasi altra cosa continua a funzionare. onDestroy(Servizio) viene chiamato, ma il processo rimane in vita per molto tempo dopo che tutto è finito. Anche se il sistema operativo manteneva in vita il processo nel caso in cui il servizio venisse riavviato, non vedo perché non fa la stessa cosa quando si interrompe prima il servizio, quindi rimuove l'app dai recenti. Il comportamento visualizzato sembra piuttosto non intuitivo, soprattutto date le recenti modifiche ai limiti di esecuzione in background, quindi sarebbe bello sapere come assicurarsi che il processo venga interrotto
David Medenjak,

@DavidMedenjak Prova a utilizzare getApplicationContext().startService(MyService.newIntent(this));invece di quello che stai utilizzando e dimmi i risultati per favore. Credo che l'attività rimanga viva perché viene utilizzato il contesto dell'attività anziché il contesto dell'applicazione. Inoltre, se stai testando su Oreo o superiore, prova a utilizzaregetApplicationContext().startforegroundService(MyService.newIntent(this));
Furkan Yurdakul il

0

Il sistema Android è noto per l'autocoscienza in termini di memoria, potenza del processore e durata dei processi delle applicazioni - decide da solo se interrompere un processo di non (lo stesso con attività e servizi)

Ecco la documentazione ufficiale su questo argomento.

Guarda cosa dice in primo piano

Ci saranno solo alcuni di questi processi nel sistema e questi verranno eliminati come ultima risorsa se la memoria è così bassa che nemmeno questi processi possono continuare a funzionare. Generalmente, a questo punto, il dispositivo ha raggiunto uno stato di paging della memoria, quindi questa azione è necessaria per mantenere reattiva l'interfaccia utente.

e processi visibili (Foreground Service è un processo visibile )

Il numero di questi processi in esecuzione nel sistema è meno limitato rispetto ai processi in primo piano, ma è ancora relativamente controllato. Questi processi sono considerati estremamente importanti e non verranno uccisi a meno che non sia necessario per mantenere in esecuzione tutti i processi in primo piano.

Significa che il sistema operativo Android avrà il processo della tua app in esecuzione finché avrà memoria sufficiente per supportare tutti i processi in primo piano . Anche se lo interrompi, il sistema potrebbe semplicemente spostarlo nella cache processi e gestirlo in modo simile alla coda. Alla fine verrà ucciso in entrambi i modi, ma di solito non spetta a te decidere. Francamente non dovresti preoccuparti di ciò che sta accadendo con il processo della tua app dopo (e fino a quando) vengono chiamati tutti i metodi del ciclo di vita di Android. Android lo sa meglio.

Ovviamente puoi uccidere il processo con android.os.Process.killProcess(android.os.Process.myPid()); interrompere ma non è consigliabile poiché interrompe il corretto ciclo di vita degli elementi Android e potrebbe non essere possibile richiamare correttamente i callback, quindi l'app potrebbe non funzionare in modo corretto in alcuni casi.

Spero che sia d'aiuto.


0

Su Android è il sistema operativo che decide quali applicazioni vengono uccise.

C'è un ordine di priorità: le
applicazioni che hanno un servizio in primo piano vengono uccise raramente, mentre altre app e servizi vengono uccisi dopo un periodo di inattività e questo è ciò che accade con la tua app.

Quando finisci il servizio in primo piano, ciò aumenta le possibilità che il sistema uccida la tua app, ma in nessun caso significa che verrà ucciso immediatamente.


0

La schermata Recenti [...] è un utente a livello di sistema che le liste di recente accessibili le attività e compiti .

Potresti avere diverse attività o attività da una singola app nell'elenco. E ' non è un Apps recente lista.

Pertanto non esiste una correlazione diretta tra gli elementi della schermata Recenti e il processo dell'app.

Ad ogni modo , se chiudi l'ultima attività della tua app e non hai nient'altro in esecuzione all'interno del processo (come un servizio in primo piano), il processo viene semplicemente ripulito.

Quindi quando interrompi un primo piano in esecuzione (di stopService()ostopSelf() non associato), il sistema pulirà anche il processo in cui era in esecuzione.

Quindi è davvero un comportamento previsto .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.