Come simulare Android uccidendo il mio processo


174

Android interromperà un processo se è in background e il sistema operativo decide che necessita delle risorse (RAM, CPU, ecc.). Devo essere in grado di simulare questo comportamento durante il test in modo da poter garantire il corretto funzionamento della mia applicazione. Voglio essere in grado di farlo in modo automatizzato in modo da poter verificare se l'applicazione si comporta correttamente ogni volta che ciò accade, il che significa che dovrò testarlo in ogni attività, ecc.

So come uccidere il mio processo. Non è questo il problema. Il problema è che quando ho ucciso mio processo (usando DDMS, adb shell kill, Process.killProcess(), etc.) Android non si riavvia allo stesso modo che avrebbe se il sistema operativo Android ha ucciso se stesso.

Se il sistema operativo Android interrompe il processo (a causa dei requisiti delle risorse), quando l'utente torna all'applicazione Android ricrea il processo e quindi ricrea l' attività principale nello stack di attività (chiamata onCreate()).

D'altra parte, se io uccido il processo, Android presuppone che l'attività in cima alla pila attività è stata che si comportano male , in modo che ricrea automaticamente il processo e poi rimuove l'attività in cima allo stack di attività e ricrea l'attività che era sotto l'attività principale (chiamando onCreate () `). Questo non è il comportamento che voglio. Voglio lo stesso comportamento di quando Android termina il processo.

Giusto per spiegare in modo pittorico, se il mio stack di attività è simile al seguente:

    ActivityA -> ActivityB -> ActivityC -> ActivityD

Se Android termina il processo e l'utente torna all'applicazione, Android ricrea il processo e crea ActivityD.

Se interrompo il processo, Android ricrea il processo e crea ActivityC.


4
Potresti non solo creare la quantità di processi necessari per uccidere il tuo in background?
Alex W,


2
@Pang Penso che ti stia perdendo il punto. So come rilevare che Android ha interrotto il processo. Ho un codice che gestisce queste condizioni. Quello che voglio fare è testare correttamente (e in modo automatizzato) questo codice . Per fare ciò, ho bisogno di un modo per provocare Android che uccida il mio processo esattamente come farebbe normalmente sotto la pressione delle risorse. La domanda collegata, sebbene interessante, non aggiunge alcun valore qui.
David Wasser,


@IgorGanapolsky Grazie per i collegamenti, ma in realtà nessuno di questi collegamenti ha soluzioni al problema.
David Wasser,

Risposte:


127

Il modo migliore per testare questo per me è stato fare questo:

  • Apri ActivityD nella tua applicazione
  • Premi il tasto Home
  • Premi Terminate Applicationnella finestra Logcat in Android Studio (questo interromperà il processo dell'app, assicurati di selezionare il dispositivo e il processo nei menu a discesa Logcat in alto)
  • Torna all'applicazione con la pressione lunga Home o le app aperte (dipende dal dispositivo)
  • L'applicazione inizierà in ActivityD ricreata (ActivityA, ActivityB, ActivityC sono morti e verrà ricreata quando torni a loro)

Su alcuni dispositivi puoi anche tornare all'applicazione (ActivityD) con Applicazioni -> Icona di avvio, ma su altri dispositivi avvierà invece ActivityA.

Questo è ciò che dicono i documenti Android al riguardo:

Normalmente, il sistema cancella un'attività (rimuove tutte le attività dallo stack sopra l'attività principale) in determinate situazioni in cui l'utente seleziona nuovamente tale attività dalla schermata principale. In genere, questo viene fatto se l'utente non ha visitato l'attività per un certo periodo di tempo, ad esempio 30 minuti.


2
Grazie per la tua risposta, ma non mi è utile perché ho bisogno di un modo automatico per farlo, per i test automatizzati.
David Wasser,

1
Questo è stato davvero utile per me.
John Roberts,

6
Questo non è automatizzato ma ha funzionato perfettamente per i miei scopi. È molto importante non saltare il passaggio 2 . È necessario inviare l'app in background prima di interrompere il processo in DDMS affinché funzioni. Per quelli che si chiedono da dove provengono le citazioni dei documenti è qui . Anche se non sono sicuro che siano effettivamente correlati all'argomento in quanto riguarda il <activity>tag nel manifest.
Tony Chan,

1
Esattamente quello di cui avevo bisogno. Grazie. Per coloro che non lo sanno, DDMS è in Eclipse, vai su Finestra -> Apri prospettiva e dovresti trovarlo lì.
Richard,

4
In alternativa puoi andare su "Opzioni di sviluppo" e impostare il limite di background su "nessun processo di background", quindi ogni volta che premi a casa il processo morirà.
Joao Gavazzi,

56

Questo sembra funzionare per me:

adb shell am kill <package_name>

Ciò è diverso da quello adb shell killmenzionato dall'OP.

Si noti che la guida per il am killcomando dice:

am kill: Kill all processes associated with <PACKAGE>.  Only kills.
  processes that are safe to kill -- that is, will not impact the user
  experience.

Quindi, non ucciderà il processo se è in primo piano. Questo sembra funzionare come voleva l'OP nel caso in cui mi allontani dalla mia app, quindi eseguirlo adb shell am kill <package_name>ucciderà l'app (l'ho confermato utilizzando pssul dispositivo). Quindi, se torno all'app, sono tornato all'attività in cui mi trovavo in precedenza, ovvero nell'esempio del PO il processo viene ricreato e crea ActivityD (piuttosto che ActivityC come la maggior parte degli altri metodi di uccisione sembrano innescare).

Mi dispiace, sono in ritardo di un paio d'anni per l'OP, ma spero che altri lo trovino utile.


Grazie! questo è quello che stavo cercando! Voglio specificare che questo comando si comporta diversamente da adb shell am force-stop. L'ultimo rimuoverà anche tutti gli In attesa relativi alla tua app (come le Notifiche) mentre il primo no.
Bonnyz,

Indica chiunque indaga sul codice sorgente del sistema operativo per vedere quale codice viene eseguito quando viene interrotto un processo per recuperare la memoria - la mia scommessa è che è lo stesso codice di am kill.
androidguy,

non funziona su Android 7.1 Samsung J5. psmostra la mia app
Valgaal

17

Un altro metodo, probabilmente uno che è script in quanto non richiede DDMS:

Configurazione unica: vai su Opzioni sviluppatore, seleziona Impostazione limite processo in background, modifica il valore da "Limite standard" a "Nessun processo in background".

Quando è necessario riavviare il processo, premere il pulsante Home. Il processo verrà interrotto (è possibile verificare in logcat / Android Monitor in studio - il processo verrà contrassegnato come [DEAD]). Quindi torna all'app usando lo switcher attività.


2
Interessante. Penso che ciò non influisca sui servizi in primo piano, giusto?
IgorGanapolsky,

Devo farlo su dispositivi reali con Android fino alla 2.3.3, quindi non è di alcun aiuto.
David Wasser,

13

Questa domanda è vecchia ma, c'è una risposta per questa domanda che non richiede adb, Android Studio ecc. L'unico requisito è API 23 o più recente.

Per simulare il riavvio dell'app dal sistema operativo, vai alle impostazioni dell'app mentre l'app è in esecuzione, disabilita (quindi puoi abilitare) un'autorizzazione e restituisci l'app dalle app recenti. Quando l'autorizzazione è disabilitata, il sistema operativo uccide l'app ma mantiene gli stati di istanza salvati. Quando l'utente restituisce l'app, vengono ricreate l'app e l'ultima attività (con stato salvato).

Il metodo "Nessun processo in background" provoca talvolta lo stesso comportamento, ma non sempre. Ad esempio, se l'app esegue un servizio in background, "Nessun processo in background" non fa nulla. Ma l'app può essere uccisa dal sistema, inclusi i suoi servizi. Il metodo di autorizzazione funziona anche se l'app ha un servizio.

Esempio:

La nostra app ha due attività. L'attività A è l'attività principale avviata dal programma di avvio. ActivityB viene avviato da ActivityA. Mostrerò solo i metodi onCreate, onStart, onStop, onDestroy. Android chiama onSaveInstanceState sempre prima di chiamare onStop, perché un'attività in stato di arresto può essere interrotta dal sistema. [ https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle]

Metodo di autorizzazione:

<start app from launcher first time>
Application onCreate
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart
<open ActivityB>
ActivityB onCreate WITHOUT savedInstance
ActivityB onStart
ActivityA onStop (the order is like this, it is stopped after new one is started)
<go settings>
ActivityB onStop
<disable a permission>
//Application is killed, but onDestroy methods are not called.
//Android does not call onDestroy methods if app will be killed.
<return app by recent apps>
Application onCreate (this is the important part. All static variables are reset.)
ActivityB onCreate WITH savedInstance (user does not notice activity is recreated)
//Note that ActivityA is not created yet, do not try to access it.
ActivityB onStart
<return ActivityA by back>
ActivityA onCreate WITH savedInstance (user does not notice activity is recreated)
ActivityA onStart
ActivityB onStop
ActivityB onDestroy
<press back again, return launcher>
ActivityA onStop
ActivityA onDestroy
<open app again>
//does not call Application onCreate, app was not killed
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart

Voglio confrontare altri metodi che sono menzionati nelle altre risposte.

Non mantenere le attività: questo non uccide l'applicazione.

<start app from launcher first time>
Application onCreate
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart
<open ActivityB>
ActivityB onCreate WITHOUT savedInstance
ActivityB onStart
ActivityA onStop
ActivityA onDestroy (do not keep)
<return launcher by home button>
ActivityB onStop
ActivityB onDestroy (do not keep) 
<retun app from recent apps>
// NO Application onCreate
ActivityB onCreate WITH savedInstance (user does not notice activity recreated)
ActivityB onStart
<return ActivityA by back>
ActivityA onCreate WITH savedInstance (user does not notice activity recreated)
ActivityA onStart
ActivityB onStop
ActivityB onDestroy
<press back again, return launcher>
ActivityA onStop
ActivityA onDestroy
<open app again>
//does not call Application onCreate, app was not killed
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart

Forza metodo di arresto: non memorizza gli stati di istanza salvati

<start app from launcher first time>
Application onCreate
ActivityA onCreate WITHOUT savedInstance
ActivityA onStart
<open ActivityB>
ActivityB onCreate WITHOUT savedInstance
ActivityB onStart
ActivityA onStop
<go settings>
ActivityB onStop
<force stop, return app from recent apps>
Application onCreate
ActivityA onCreate WITHOUT savedInstance 
//This is important part, app is destroyed by user.
//Root activity of the task is started, not the top activity.
//Also there is no savedInstance.

~ "L' app può essere uccisa dal sistema incluso il suo servizio ". Servizio non in primo piano ...
IgorGanapolsky,

@DavidWasser Leggi le specifiche! developer.android.com/guide/components/services.html#Foreground Un servizio in primo piano è un servizio di cui l'utente è attivamente consapevole e non è un candidato per il sistema da uccidere quando la memoria è insufficiente.
IgorGanapolsky,

3
La documentazione di @IgorGanapolsky è buona, specialmente se è completa e corretta (cosa che sfortunatamente non lo è), ma di solito faccio più affidamento su osservazioni personali effettive. Ho visto un primo piano Serviceucciso molte volte. Anche se il sistema non ha poca memoria. La maggior parte dei produttori di dispositivi ha scritto le proprie "ottimizzazioni" e "miglioramenti" sul sistema operativo Android per risparmiare la batteria. Molti dispositivi hanno "killer" molto più aggressivi rispetto allo standard Android.
David Wasser,

Osservazione di @DavidWasser Fair. Quindi, dopo tutti questi anni, hai trovato una soluzione?
IgorGanapolsky,

@IgorGanapolsky No. Non ho trovato alcuna soluzione a questo problema. Ecco perché la domanda è ancora aperta.
David Wasser,

7

Sono in ritardo alla festa e molti prima di me hanno dato la stessa risposta corretta ma per semplificare per chiunque venga dopo di me basta premere il tasto home ed eseguire questo comando:

adb shell ps | grep <package name> | awk '{print $2}' | xargs adb shell run-as <package name again> kill

L'app non perderà lo stato e dalla mia esperienza funziona allo stesso modo in cui il sistema operativo ha ucciso l'app in background. Funziona solo con applicazioni integrate di debug


Ricevo'grep' is not recognized as an internal or external command, operable program or batch file.
Dale il

Ma l'ho fatto mentre ero nella shell run-as <package name> kill 7379, ma mi ha messo all'attività precedente, non all'attività che avevo quando ho premuto il pulsante home.
Dale,

6

Ecco come lo fai in Android Studio.

  1. Avere il dispositivo in modalità debug collegato al computer.
  2. Apri l'app sul tuo dispositivo e vai a qualsiasi attività tu voglia testare il "Ritorno ad essa dai morti".
  3. Premi il pulsante Home sul dispositivo.
  4. In Android Studio vai su Android Monitor -> Monitor e premi l'icona Termina applicazione.
  5. Ora puoi tornare alla tua app tramite le app recenti o facendo clic sull'icona di avvio, il comportamento è stato lo stesso nei miei test.

2
Questo non è di alcun aiuto. Devo farlo a livello di codice, all'interno di una suite di test. Ma grazie comunque.
David Wasser,

Inoltre, questa risposta è praticamente la stessa della risposta di Mark.
David Wasser,

Qualche idea su come farlo tramite UIAutomator o Espresso?
IgorGanapolsky,

Non riesco a trovare Android Monitor - Monitors. Dev'essere qualcosa di cui si sono sbarazzati. Sono in v 3.2.1
Dale

1
@Dale Android Device Monitor è stato deprecato in Android Studio 3.1 e rimosso da Android Studio 3.2.
Valgaal

5

Metti l'applicazione in background con il pulsante HOME

Seleziona il processo in modalità "Logcat" in Android Studio, quindi fai clic su Termina applicazione nell'angolo in basso a sinistra

pulsante termina

Ora avvia l'app dal programma di avvio sul dispositivo Android


EDIT: secondo Internet, funziona anche quanto segue:

 adb shell am kill [my-package-name]

MODIFICA dal futuro: Qualcosa da notare, c'è stato un cambiamento in Android Studio 4.0, se usi Runda AS, allora Terminateemetterà un Force Stop.

Tuttavia, se si avvia successivamente dal programma di avvio e POI si tenta di simularlo in questo modo, si otterranno i risultati desiderati (comportamento di memoria insufficiente).


Questa è una risposta duplicata
Onik,

@Onik Tutti gli altri hanno un sacco di lanugine inutili come ddms e quant'altro. Sebbene tecnicamente sì, stackoverflow.com/a/41975750/2413303 dice la stessa cosa. Forse dovrei aggiungere delle foto.
EpicPandaForce,

Questo non è utile Ho un cablaggio di prova e non posso eseguire queste azioni dal cablaggio di prova. Inoltre, il comportamento non è lo stesso di quello che succede quando Android termina il processo.
David Wasser,

the behaviour is not the same as what happens when Android kills the processsi lo è
EpicPandaForce

Questo processo mi mostra l'attività precedente, non l'attività di quando viene premuto il pulsante Home.
Dale,

2

È possibile eseguire i passaggi successivi per riprodurre il comportamento desiderato:

  1. Apri l'app, passa all'attività principale
  2. Utilizza il pannello delle notifiche per accedere a qualsiasi altra applicazione a schermo intero (ad esempio, alle impostazioni di sistema - nell'angolo in alto a destra)
  3. Termina il processo di candidatura
  4. Premi il pulsante Indietro

1
Grazie per la tua risposta, ma non mi è utile perché ho bisogno di un modo automatico per farlo, per i test automatizzati.
David Wasser,

Quindi, questo è l'unico metodo che ho trovato simulerebbe effettivamente la memoria Android che cancella e uccide la tua app (il mio livello API è 19, quindi non posso usare il comando send-trim-memory). Altri comandi come adb shell sono force-stop com.my.app.package o kill, non riprodurranno lo stesso processo esatto che seguendo la procedura sopra!
Mark Garcia,

2

Nelle opzioni per gli sviluppatori in Impostazioni, seleziona "Non conservare attività", che distruggerà le attività non appena ti allontani da esse.

Nota: come per un utile commento di seguito, utilizzalo solo se non ti interessa l'eliminazione dei valori statici.


Questo è effettivamente lo stesso della soluzione già pubblicata - e respinta - un anno fa. L'unica differenza sembra essere l'applicazione utilizzata per impostarlo su un emulatore, rispetto ai telefoni più recenti che lo supportano.
Chris Stratton,

1
Mi dispiace, come dice Chris Stratton, questo è praticamente lo stesso suggerimento dell'altra risposta. Non si tratta delle attività di finitura di Android. Si tratta di Android che uccide l'intero processo (cosa che fa, abbastanza regolarmente ed efficacemente, specialmente sui dispositivi HTC con Android 4.x).
David Wasser,

6
Questo è quasi buono ma non ucciderà il processo, distruggerà solo l'attività. Cosa significa? La tua attività verrà aperta con savedInstanceState ma tutte le variabili statiche sono ancora in corso. Dopo l'uccisione del processo vengono cancellate anche tutte le variabili statiche.
Segna il

1

Premi il pulsante Home e metti prima l'app in background. Quindi interrompere o interrompere il processo da DDMS o ADB.


Grazie per la tua risposta, ma non mi è utile perché ho bisogno di un modo automatico per farlo, per i test automatizzati.
David Wasser,

Android non ucciderà mai il tuo processo se la tua attività è attualmente in primo piano. Stai provando a provare una condizione che non si verificherà mai. Se la tua attività è in background, il suo stato è già stato salvato e non vi è alcuna differenza tra l'uccisione manuale e Android in memoria insufficiente, ovvero il processo è appena terminato; non c'è niente di speciale in un'uccisione di memoria insufficiente. Quando la memoria sarà di nuovo disponibile, i servizi appiccicosi verranno riavviati (tranne su 4.4) e quando si tocca l'icona o l'attività recente, lo stato dello stack e dell'attività verrà ripristinato.
Monstieur,

3
Nel tuo esempio, torna all'attività C perché hai interrotto il processo mentre l'attività D era visibile sullo schermo (ciò non si verificherà mai anche con memoria insufficiente) e lo stato dell'attività C è stato salvato poiché è in background. Il processo verrà interrotto solo se non è in primo piano, ovvero lo stato dell'attività D sarebbe stato salvato quando è passato in background e quindi verrà ripristinato anche se il processo viene interrotto. Per eseguire i test su ogni attività, DEVI inviare l'app in background prima di ucciderla.
Monstieur,

Cosa intendevi con ~ " e mettevi prima l'app in background "?
IgorGanapolsky,

1

Puoi anche connetterti al tuo dispositivo / emulatore dal terminale con adb shell, quindi ottenere PID del processo con ps | grep <your_package_nameed eseguire kill -9 <pid>. Quindi apri l'app ridotta a icona dal selettore di app recenti e riavvierà l'ultima attività


È lo stesso di Android che uccide un processo in condizioni di memoria insufficiente? Penso che OP volesse specificatamente che ...
IgorGanapolsky,

1
@IgorGanapolsky è teoricamente, anche se non è possibile farlo su un dispositivo reale se non è rootato.
Fran Marzoa,

0

La radice del tuo problema sembra essere che sei Activityin primo piano quando uccidi il processo.

Puoi osservarlo premendo stop in DDMS quando Activityè visibile (succede esattamente quello che descrivi) e confrontandolo con premendo stop dopo casa e successivamente tornando all'app.

Assicurati solo di in moveTaskToBack(true)qualche modo nei tuoi test.


0

Non sono sicuro che questa sia la risposta che stai cercando, è più come una logica pensa.

Non penso che tu possa davvero fare un test completamente automatizzato, l'unico modo per simularlo, sarà ricrearlo, AKA ha così tante attività che Android ucciderà la tua applicazione.

Quindi la mia idea o suggerimento è quello di creare un'altra piccola app, che continua a spuntare nuove attività, fino a quando Android esaurisce la memoria e inizia a uccidere il processo in background.

Qualcosa nella linea:

Avvia attività i -> Controlla il processo in esecuzione se l'app è nell'elenco, incrementa i e riavvia il ciclo senza chiudere l'attività corrente, altrimenti -> diminuisci i e chiudi l'attività corrente, torna alla precedente e ricontrolla ...


0

Quando il processo di applicazione termina, Android passa attraverso i record delle attività (le voci rappresentano le attività nello stack della cronologia) e decide quali conservare nella cronologia e quali rimuovere da essa.

Uno dei punti chiave qui è il ActivityRecordcampo chiamato haveState, che gli ingegneri di Android Framework descrivono come "abbiamo ottenuto l'ultimo stato di attività?".

Per impostazione predefinita, Android considera che l'attività ha uno stato. L'attività diventa apolide quando l'applicazione segnala al servizio di gestione attività l'attività che l'attività è stata ripresa e ciò è valido fino a quando l'applicazione non notifica al framework che l'attività è entrata nello stato Interrotto. In parole semplici, il haveStatevalore è falsetra l'attività onResume()viene chiamata e onStop()o onSaveInstanceState()viene invocata, a seconda della versione di destinazione dell'applicazione.

Se interrompo il processo, Android ricrea il processo e crea ActivityC.

In questo caso ActivityD non ha android:stateNotNeeded="true"attributo nel manifest dell'applicazione ed è attualmente in esecuzione in primo piano, quindi Android lo rimuove dalla cronologia poiché il sistema non ha ottenuto il suo ultimo stato.

Come simulare Android uccidendo il mio processo

Come è stato menzionato più volte, puoi semplicemente spostare l'applicazione in background, quindi l'attività principale nello stack di attività salverà il suo stato e, successivamente, puoi interrompere il processo dell'applicazione tramite Android Debug Bridge, Android Studio o utilizzando il Limite dei processi in background nelle Opzioni sviluppatore. Successivamente, la tua attività recente verrà ricreata correttamente.

Nonostante ciò, esiste anche un altro modo semplice per testare lo scenario di morte del processo dell'applicazione. Sapendo tutto quanto sopra descritto e il fatto che se si avvia una nuova ActivityE dall'attività ActivityD attualmente in esecuzione, il onStop()callback di ActivityD viene invocato solo dopo il onResume()metodo ActivityE , è possibile eseguire il trucco seguente.

class TerminatorActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val isPrePie = applicationInfo.targetSdkVersion < Build.VERSION_CODES.P
        val callbacks = TerminatorLifecycleCallbacks(isPrePie)
        (applicationContext as Application).registerActivityLifecycleCallbacks(callbacks)
    }

    private class TerminatorLifecycleCallbacks(
        // Before P onSaveInstanceState() was called before onStop(), starting with P it's
        // called after
        // Used to schedule the death as app reports server that activity has stopped
        // after the latest of these was invoked
        private val isPrePie: Boolean
    ) : ActivityLifecycleCallbacksDefault {

        private val handler = Handler(Looper.getMainLooper())

        override fun onActivityPostStopped(activity: Activity) {
            if (isPrePie) {
                terminate()
            }
        }

        override fun onActivityPostSaveInstanceState(activity: Activity, outState: Bundle) {
            if (!isPrePie) {
                terminate()
            }
        }

        fun terminate() {
            handler.postDelayed(
                {
                    Process.killProcess(Process.myPid()) // This is the end... 
                },
                LAST_MILLIS
            )
        }

        companion object {
            // Let's wait for a while, so app can report and server can handle the update
            const val LAST_MILLIS = 100L
        }

    }

    private interface ActivityLifecycleCallbacksDefault : Application.ActivityLifecycleCallbacks {
        override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
        override fun onActivityStarted(activity: Activity) {}
        override fun onActivityResumed(activity: Activity) {}
        override fun onActivityPaused(activity: Activity) {}
        override fun onActivityStopped(activity: Activity) {}
        override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
        override fun onActivityDestroyed(activity: Activity) {}
    }
}

Quindi inizia TerminatorActivityquando vuoi uccidere l'applicazione.

Alla fine c'è uno strumento leggero che semplifica il test della morte del processo dell'applicazione, chiamato Venom .

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.