Cómo simular Android matando mi proceso

174

Android eliminará un proceso si está en segundo plano y el sistema operativo decide que necesita los recursos (RAM, CPU, etc.). Necesito poder simular este comportamiento durante las pruebas para asegurarme de que mi aplicación se esté comportando correctamente. Quiero poder hacer esto de manera automatizada para poder probar si la aplicación se comporta correctamente cada vez que esto sucede, lo que significa que tendré que probar esto en cada actividad, etc.

Sé cómo matar mi proceso. Ese no es el problema. El problema es que cuando mato mi proceso (utilizando DDMS, adb shell kill, Process.killProcess(), etc.) Android no se reinicia de la misma manera que lo haría si el sistema operativo Android había matado él mismo.

Si el sistema operativo Android elimina el proceso (debido a los requisitos de recursos), cuando el usuario regrese a la aplicación, Android recreará el proceso y luego recreará la actividad principal en la pila de actividades (llamada onCreate()).

Por otro lado, si me mato el proceso, Android asume que la actividad en la parte superior de la pila de actividad se comporten mal , por lo que vuelve a crear automáticamente el proceso y luego elimina la actividad de la parte superior de la pila de actividad y recrea la actividad que estaba debajo la actividad principal (llamando a onCreate () `). Este no es el comportamiento que quiero. Quiero el mismo comportamiento que cuando Android mata el proceso.

Solo para explicar gráficamente, si mi pila de actividades se ve así:

    ActivityA -> ActivityB -> ActivityC -> ActivityD

Si Android elimina el proceso y el usuario vuelve a la aplicación, Android vuelve a crear el proceso y crea ActivityD.

Si elimino el proceso, Android recrea el proceso y crea ActivityC.

David Wasser
fuente
44
¿No podrías simplemente crear la cantidad de procesos necesarios para matar el tuyo en segundo plano?
Alex W
2
@Pang Creo que te estás perdiendo el punto. Sé cómo detectar que Android ha matado el proceso. Tengo un código que maneja estas condiciones. Lo que quiero hacer es probar correctamente (y de forma automatizada) este código . Para hacer eso, necesito alguna forma de provocar que Android mate mi proceso exactamente de la misma manera que lo haría normalmente bajo la presión de los recursos. La pregunta vinculada, aunque interesante, no agrega ningún valor aquí.
David Wasser
@IgorGanapolsky Gracias por los enlaces, pero en realidad ninguno de esos enlaces tiene soluciones al problema.
David Wasser

Respuestas:

127

La mejor manera de probar esto para mí fue hacer esto:

  • Abra ActivityD en su aplicación
  • Presione el botón de inicio
  • Presione Terminate Applicationen la ventana Logcat en Android Studio (esto matará el proceso de la aplicación, asegúrese de seleccionar su dispositivo y procesar en los menús desplegables de Logcat en la parte superior)
  • Regrese a la aplicación con Inicio presionada prolongadamente o aplicaciones abiertas (depende del dispositivo)
  • La aplicación comenzará en la Actividad D recreada (la Actividad A, la Actividad B, la Actividad C están muertas y se volverán a crear cuando vuelva a ellas)

En algunos dispositivos, también puede volver a la aplicación (ActivityD) con Aplicaciones -> Su icono de inicio, pero en otros dispositivos iniciará la Actividad A.

Esto es lo que dicen los documentos de Android al respecto:

Normalmente, el sistema borra una tarea (elimina todas las actividades de la pila sobre la actividad raíz) en ciertas situaciones cuando el usuario vuelve a seleccionar esa tarea desde la pantalla de inicio. Por lo general, esto se hace si el usuario no ha visitado la tarea durante un período de tiempo determinado, como 30 minutos.

marca
fuente
2
Gracias por su respuesta, pero no me es útil porque necesito una forma automatizada de hacer esto, para las pruebas automatizadas.
David Wasser
1
Esto fue realmente útil para mí.
John Roberts
66
Esto no está automatizado, pero funcionó perfectamente para mis propósitos. Es muy importante no omitir el paso 2 . Debe enviar la aplicación a un segundo plano antes de detener el proceso en DDMS para que esto funcione. Para aquellos que se preguntan de dónde provienen las citas del documento, está aquí . Aunque no estoy seguro de que estén realmente relacionados con el tema, ya que se trata de la <activity>etiqueta en el manifiesto.
Tony Chan
1
Exactamente lo que necesitaba. Gracias. Para aquellos que no saben, DDMS está en Eclipse, vaya a Ventana -> Abrir perspectiva, y debería encontrarlo allí.
Richard
44
Alternativamente, puede ir a "Opciones de desarrollo" y establecer el límite de fondo en "sin procesos en segundo plano" y luego, cada vez que presiona el botón de inicio, el proceso morirá.
Joao Gavazzi
56

Esto parece funcionar para mí:

adb shell am kill <package_name>

Esto es diferente a lo adb shell killmencionado por el OP.

Tenga en cuenta que la ayuda para el 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.

Por lo tanto, no matará el proceso si está en primer plano. Esto parece funcionar como lo quería el OP, ya que si navego fuera de mi aplicación, la ejecuto adb shell am kill <package_name>matará la aplicación (lo he confirmado usando psen el dispositivo). Luego, si vuelvo a la aplicación, vuelvo a la actividad en la que estaba anteriormente, es decir, en el ejemplo del OP, el proceso se recrea y crea ActivityD (en lugar de ActivityC, como la mayoría de los otros métodos de asesinato parecen desencadenarse).

Lo siento, llevo un par de años tarde para el OP, pero espero que otros encuentren esto útil.

HexAndBugs
fuente
¡Gracias! ¡Esto es lo que estaba buscando! Quiero especificar que este comando se comporta de manera diferente a adb shell am force-stop. El último también eliminará cualquier Intento pendiente relacionado con su aplicación (como Notificaciones) mientras que el primero no.
bonnyz
Señala a cualquiera que investigue el código fuente del sistema operativo para ver qué código se ejecuta cuando mata un proceso para recuperar memoria; mi apuesta es que es el mismo código que am kill.
androidguy
no funciona en Android 7.1 Samsung J5. psmuestra mi aplicación
Valgaal
17

Otro método, probablemente uno que sea programable ya que no requiere DDMS:

Configuración única: vaya a Opciones de desarrollador, seleccione Configuración de límite de proceso en segundo plano, cambie el valor de 'Límite estándar' a 'Sin procesos en segundo plano'.

Cuando necesite reiniciar el proceso, presione el botón de inicio. El proceso se eliminará (puede verificarlo en logcat / Android Monitor en el estudio; el proceso se marcará [MUERTO]). Luego vuelva a la aplicación utilizando el conmutador de tareas.

Merk
fuente
2
Interesante. Creo que eso no afecta a los servicios en primer plano, ¿verdad?
IgorGanapolsky
Necesito hacer esto en dispositivos reales con Android tan antiguo como 2.3.3, así que esto no es de ninguna ayuda.
David Wasser
13

Esta pregunta es antigua, pero hay una respuesta para esta pregunta que no requiere adb, Android Studio, etc. El único requisito es API 23 o más reciente.

Para simular el reinicio de la aplicación por sistema operativo, vaya a la configuración de la aplicación mientras su aplicación se está ejecutando, deshabilite (luego puede habilitar) un permiso y devuelva la aplicación desde aplicaciones recientes. Cuando se deshabilita el permiso, el sistema operativo mata la aplicación pero mantiene los estados de instancia guardados. Cuando el usuario devuelve la aplicación, la aplicación y la última actividad (con estado guardado) se recrean.

El método 'Sin procesos en segundo plano' a veces causa el mismo comportamiento, pero no siempre. Por ejemplo, si la aplicación ejecuta un servicio en segundo plano, "No hay procesos en segundo plano" no hace nada. Pero la aplicación puede ser eliminada por el sistema, incluidos sus servicios. El método de permiso funciona incluso si la aplicación tiene un servicio.

Ejemplo:

Nuestra aplicación tiene dos actividades. La actividad A es la actividad principal que se inicia desde el iniciador. La actividad B se inicia desde la actividad A. Solo mostraré los métodos onCreate, onStart, onStop, onDestroy. Android llama a onSaveInstanceState siempre antes de llamar a onStop, porque el sistema puede eliminar una actividad que está en estado de detención. [ https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle]

Método de permiso:

<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

Quiero comparar otros métodos que se mencionan en las otras respuestas.

No mantener actividades: esto no mata la aplicación.

<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

Método de detención forzada: no almacena estados de instancia guardados

<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.
fthdgn
fuente
~ "la aplicación puede ser eliminada por el sistema, incluido su servicio ". Not
Foreground
@DavidWasser ¡Lee las especificaciones! developer.android.com/guide/components/services.html#Foreground Un servicio en primer plano es un servicio que el usuario conoce activamente y no es un candidato para que el sistema elimine cuando tiene poca memoria.
IgorGanapolsky
3
La documentación de @IgorGanapolsky es buena, especialmente si está completa y es correcta (lo que desafortunadamente no es), pero por lo general confío más en observaciones personales reales. He visto un primer plano Serviceasesinado muchas veces. Incluso si el sistema no tiene poca memoria. La mayoría de los fabricantes de dispositivos han escrito sus propias "optimizaciones" y "mejoras" para el sistema operativo Android con el fin de ahorrar batería. Muchos dispositivos tienen "asesinos" mucho más agresivos que el Android estándar.
David Wasser
@DavidWasser Observación justa. Entonces, después de todos estos años, ¿se te ocurrió una solución?
IgorGanapolsky
@IgorGanapolsky No. No he encontrado ninguna solución a este problema. Por eso la pregunta sigue abierta.
David Wasser
7

Llegué muy tarde a la fiesta y varios antes que yo dieron la misma respuesta correcta, pero para simplificar para quien venga después de mí, simplemente presione el botón de inicio y ejecute este comando:

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

La aplicación no perderá estado y, según mi propia experiencia, funciona de la misma manera que el sistema operativo eliminó la aplicación en segundo plano. Esto funciona solo para depurar aplicaciones compiladas

Hirschen
fuente
Obtengo'grep' is not recognized as an internal or external command, operable program or batch file.
Dale
Pero lo hice mientras estaba en el shell run-as <package name> kill 7379, pero me puso en la actividad anterior, no en la actividad en la que estaba cuando presioné el botón de inicio.
Dale
6

Así es como lo haces en Android Studio.

  1. Tenga su dispositivo en modo de depuración conectado a su computadora.
  2. Abra la aplicación en su dispositivo y vaya a la actividad que quiera probar "Volver a ella desde la muerte".
  3. Presione el botón de Inicio en su dispositivo.
  4. En Android Studio, vaya a Monitor de Android -> Monitores y presione el icono Terminar aplicación.
  5. Ahora puede volver a su aplicación a través de las aplicaciones recientes o al hacer clic en su icono de inicio, el comportamiento ha sido el mismo en mis pruebas.
dbar
fuente
2
Esto no es de ninguna ayuda. Necesito hacer esto programáticamente, dentro de un conjunto de pruebas. Pero gracias de todos modos.
David Wasser
Además, esta respuesta es más o menos la misma que la respuesta de Mark.
David Wasser
¿Alguna idea de cómo hacer esto a través de UIAutomator o Espresso?
IgorGanapolsky
No puedo encontrar Android Monitor - Monitors. Debe ser algo de lo que se hayan librado. Estoy en v 3.2.1
Dale
1
@Dale Android Device Monitor quedó en desuso en Android Studio 3.1 y se eliminó de Android Studio 3.2.
Valgaal
5

Ponga la aplicación en segundo plano con el botón INICIO

Seleccione su proceso en el modo "Logcat" en Android Studio, luego haga clic en Terminar aplicación en la esquina inferior izquierda

botón de terminar

Ahora inicie su aplicación desde el iniciador en el dispositivo Android


EDITAR: Según Internet, lo siguiente también funciona:

 adb shell am kill [my-package-name]

EDITAR desde el futuro: Algo a tener en cuenta, ha habido un cambio en Android Studio 4.0, si lo usa Rundesde AS, Terminateemitirá un Force Stop.

Sin embargo, si inicia desde el iniciador después, y ENTONCES intenta simularlo de esta manera, obtendrá los resultados que desea (comportamiento con poca memoria).

EpicPandaForce
fuente
Esta es una respuesta duplicada
Onik
@Onik Todos los demás tienen un montón de pelusas innecesarias como ddms y lo que sea. Aunque técnicamente sí, stackoverflow.com/a/41975750/2413303 dice lo mismo. Tal vez debería agregar fotos.
EpicPandaForce
Esto no es útil. Tengo un arnés de prueba y no puedo ejecutar estas acciones desde el arnés de prueba. Además, el comportamiento no es el mismo que sucede cuando Android mata el proceso.
David Wasser
the behaviour is not the same as what happens when Android kills the processsí lo es
EpicPandaForce
Este proceso me muestra la actividad anterior, no la actividad de cuando se presiona el botón de inicio.
Dale
2

Puede realizar los siguientes pasos para reproducir el comportamiento buscado:

  1. Abra su aplicación, navegue a la actividad principal
  2. Use el panel de notificaciones para navegar a cualquier otra aplicación de pantalla completa (por ejemplo, a la configuración del sistema, en la esquina superior derecha)
  3. Mata tu proceso de solicitud
  4. Presione el botón de retroceso
Igor Kostomin
fuente
1
Gracias por su respuesta, pero no me es útil porque necesito una forma automatizada de hacer esto, para las pruebas automatizadas.
David Wasser
Por lo tanto, este es el único método que encontré que en realidad simularía Android borrando memoria y matando su aplicación (mi nivel de API es 19, por lo que no puedo usar el comando send-trim-memory). Otros comandos, como adb shell am force-stop com.my.app.package o kill, no reproducirán el mismo proceso exacto que siguiendo el procedimiento anterior.
Mark Garcia
2

En las Opciones de desarrollador en Configuración, seleccione 'No mantener actividades', que destruirá las actividades tan pronto como se aleje de ellas.

Nota: Según un comentario útil a continuación, solo use esto si no le importa que se borren los valores estáticos.

MSpeed
fuente
Esto es efectivamente lo mismo que la solución ya publicada, y rechazada, hace un año. La única diferencia parece ser la aplicación utilizada para configurarlo en un emulador, frente a los teléfonos más recientes que lo admiten.
Chris Stratton
1
Lo sentimos, como dice Chris Stratton, esta es más o menos la misma sugerencia que la otra respuesta. No se trata de las actividades de acabado de Android. Se trata de que Android elimine todo el proceso (lo que hace, con bastante regularidad y eficacia, especialmente en dispositivos HTC con Android 4.x).
David Wasser
66
Este es casi bueno, pero no matará el proceso, solo destruye la actividad. Qué significa eso? Su actividad se abrirá con saveInstanceState pero todas las variables estáticas todavía están en proceso. Después del proceso, todas las variables estáticas también se borran.
Mark
1

Presiona el botón de Inicio y coloca primero la aplicación en segundo plano. Luego, detenga o elimine el proceso desde DDMS o ADB.

Monstieur
fuente
Gracias por su respuesta, pero no me es útil porque necesito una forma automatizada de hacer esto, para las pruebas automatizadas.
David Wasser
Android nunca matará tu proceso si tu actividad está actualmente en primer plano. Estás tratando de probar una condición que nunca ocurrirá. Si su actividad está en segundo plano, entonces su estado ya se ha guardado y no hay diferencia entre matarlo manualmente y Android matarlo con poca memoria, es decir, el proceso se acaba de matar; No hay nada de especial en matar una memoria baja. Cuando la memoria esté disponible nuevamente, sus servicios fijos se reiniciarán (excepto en 4.4) y cuando toque el icono o la tarea reciente, se restaurará la pila y el estado de la actividad.
Monstieur
3
En su ejemplo, vuelve a la Actividad C porque eliminó el proceso mientras la Actividad D estaba visible en la pantalla (esto nunca ocurrirá incluso con poca memoria) y el estado de la Actividad C se guardó ya que está en segundo plano. Su proceso solo se eliminará si no está en primer plano, es decir, el estado de la Actividad D se habría guardado cuando pasó a segundo plano y, por lo tanto, se restaurará incluso si se elimina su proceso. Para realizar sus pruebas en cada actividad, DEBE enviar la aplicación a un segundo plano antes de matarla.
Monstieur
¿Qué quisiste decir con ~ " y poner la aplicación en segundo plano "?
IgorGanapolsky
1

También puede conectarse a su dispositivo / emulador desde la terminal con adb shell, luego obtener el PID de su proceso ps | grep <your_package_namey ejecutarlo kill -9 <pid>. Luego abra su aplicación minimizada desde el selector de aplicaciones recientes y reiniciará la última actividad

qbasso
fuente
¿Es lo mismo que Android matando un proceso en condiciones de poca memoria? Creo que OP específicamente quería eso ...
IgorGanapolsky
1
Teóricamente, @IgorGanapolsky lo es, aunque no podría hacerlo en un dispositivo real si no está rooteado.
Fran Marzoa
0

La raíz de su problema parece ser que Activityestá en primer plano cuando termina el proceso.

Puede observar esto presionando detener en DDMS cuando Activityesté visible (sucede exactamente lo que usted describe) y comparándolo con presionar detener después de regresar a casa y luego regresar a la aplicación.

Solo asegúrese de moveTaskToBack(true)alguna manera en sus pruebas.

MaciejGórski
fuente
0

No estoy seguro de que esta sea la respuesta que estás buscando, es más como un pensamiento lógico.

No creo que realmente puedas hacer una prueba completamente automatizada, la única forma de simularla será recrearla, también tiene tantas actividades que Android matará tu aplicación.

Entonces, mi idea o sugerencia es crear otra aplicación pequeña, que siga apareciendo nuevas actividades, hasta que Android se quede sin memoria y comience a matar el proceso en segundo plano.

Algo entre la línea:

Inicie la actividad i -> Verifique el proceso de ejecución si la aplicación está en la lista, incremente i y reinicie el ciclo sin cerrar la actividad actual, de lo contrario -> disminuya i y cierre la actividad actual, regrese a la anterior y vuelva a verificar ...

Emil Borconi
fuente
0

Cuando el proceso de solicitud finaliza, Android revisa los registros de actividades (las entradas representan actividades en la pila del historial) y decide cuáles mantener en el historial y cuáles eliminar de él.

Uno de los puntos clave aquí es el ActivityRecordcampo llamado haveState, que los ingenieros de Android Framework describen como "¿obtuvimos el último estado de actividad?".

Por defecto, Android considera que la actividad tiene un estado. La actividad deja de tener estado cuando la aplicación informa al servicio del administrador de tareas de actividad que la actividad se ha reanudado y esto es válido hasta que la aplicación notifica al marco que la actividad ha entrado en el estado Detenido. En palabras simples, el haveStatevalor falseentre la actividad onResume()se llama onStop()o onSaveInstanceState()se invoca, dependiendo de la versión de destino de la aplicación.

Si elimino el proceso, Android recrea el proceso y crea ActivityC.

En este caso, ActivityD no tiene android:stateNotNeeded="true"atributo en el manifiesto de la aplicación y actualmente se está ejecutando en primer plano, por lo que Android lo elimina del historial ya que el sistema no ha alcanzado su último estado.

Cómo simular Android matando mi proceso

Como se mencionó varias veces, simplemente puede mover la aplicación a un segundo plano, por lo que la actividad principal en la pila de reserva de actividad guardará su estado, y después de eso puede matar el proceso de la aplicación a través de Android Debug Bridge, Android Studio o usando el Propiedad de límite de procesos en segundo plano en las Opciones de desarrollador. Después de eso, su actividad reciente se recreará con éxito.

A pesar de eso, también hay otra forma simple de probar el escenario de muerte del proceso de solicitud. Conociendo todo lo descrito anteriormente y el hecho de que si inicia una nueva ActivityE desde la ActivityD que se está ejecutando actualmente, la onStop()devolución de llamada de ActivityD se invoca solo después del onResume()método ActivityE , puede hacer el siguiente truco.

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

Luego, simplemente comience TerminatorActivitycuando desee eliminar la aplicación.

Al final hay una herramienta liviana que simplifica la prueba de la muerte de su proceso de aplicación, llamada Venom .

ivkil
fuente