Как симулировать Android, убивающий мой процесс

174

Android убьет процесс, если он находится в фоновом режиме, а ОС решит, что ему нужны ресурсы (оперативная память, процессор и т. Д.). Мне нужно иметь возможность смоделировать это поведение во время тестирования, чтобы убедиться, что мое приложение работает правильно. Я хочу быть в состоянии сделать это в автоматическом режиме, чтобы я мог проверить, правильно ли приложение ведет себя, когда это происходит, что означает, что мне придется проверять это в каждом действии и т. Д.

Я знаю, как убить мой процесс. Это не проблема. Проблема заключается в том, что когда я убиваю мой процесс ( с использованием DDMS, adb shell kill, Process.killProcess()и т.д.) Android не перезапускает его таким же образом , что это будет , если Android OS убившую его сам.

Если ОС Android убивает процесс (из-за требований к ресурсам), когда пользователь возвращается в приложение, Android воссоздает процесс, а затем воссоздает верхнюю активность в стеке активности (вызов onCreate()).

С другой стороны, если я убиваю процесс, Android предполагает, что активность в верхней части стека активности велась плохо , поэтому он автоматически воссоздает процесс, а затем удаляет верхнюю активность из стека активности и воссоздает действие, находившееся под ним. верхняя активность (вызывая onCreate () `). Это не то поведение, которое я хочу. Я хочу такое же поведение, как когда Android убивает процесс.

Просто для иллюстрации, если мой стек активности выглядит так:

    ActivityA -> ActivityB -> ActivityC -> ActivityD

Если Android убивает процесс и пользователь возвращается в приложение, Android заново создает процесс и создает ActivityD.

Если я убью процесс, Android воссоздает процесс и создает ActivityC.

Дэвид Вассер
источник
4
Не могли бы вы просто создать количество процессов, необходимых для уничтожения ваших в фоновом режиме?
Алекс W
2
@Панг Я думаю, вы упускаете суть. Я знаю, как обнаружить, что Android убил процесс. У меня есть код, который обрабатывает эти условия. Что я хочу сделать, это правильно (и в автоматическом режиме) протестировать этот код . Чтобы сделать это, мне нужен какой-то способ провоцировать Android на то, чтобы убить мой процесс точно так же, как это обычно происходит под давлением ресурсов. Связанный вопрос, хотя и интересный, здесь не добавляет никакой ценности.
Дэвид Вассер
@IgorGanapolsky Спасибо за ссылки, но на самом деле ни одна из этих ссылок не имеет решения проблемы.
Дэвид Вассер

Ответы:

127

Лучший способ проверить это для меня:

  • Откройте ActivityD в вашем приложении
  • Нажмите кнопку «Домой»
  • Нажмите Terminate Applicationв окне Logcat в Android Studio (это убьет процесс приложения, убедитесь, что вы выбрали свое устройство и процесс в раскрывающихся списках Logcat вверху)
  • Вернитесь к приложению, нажав Home или открыв приложения (зависит от устройства)
  • Приложение запустится в воссозданном ActivityD (ActivityA, ActivityB, ActivityC мертвы и будут воссозданы, когда вы вернетесь к ним)

На некоторых устройствах вы также можете вернуться к приложению (ActivityD) с помощью Приложения -> Значок запуска, но на других устройствах вместо этого будет запускаться ActivityA.

Вот что говорят об этом Android-документы:

Обычно система очищает задачу (удаляет все действия из стека выше корневого действия) в определенных ситуациях, когда пользователь повторно выбирает эту задачу на главном экране. Как правило, это делается, если пользователь не посещал задачу в течение определенного времени, например 30 минут.

отметка
источник
2
Спасибо за ваш ответ, но он мне не нужен, потому что мне нужен автоматизированный способ сделать это для автоматизированного тестирования.
Дэвид Вассер
1
Это было действительно полезно для меня.
Джон Робертс
6
Это не автоматизировано, но оно отлично работает для моих целей. Очень важно не пропустить шаг 2 . Вы должны отправить приложение в фоновом режиме, прежде чем остановить процесс в DDMS, чтобы это работало. Для тех, кто интересуется, откуда взялись цитаты из документов, он здесь . Хотя я не уверен, что они на самом деле имеют отношение к теме, так как речь идет о <activity>теге в манифесте.
Тони Чан
1
Именно то, что мне было нужно. Спасибо. Для тех, кто не знает, DDMS находится в Eclipse, перейдите в Window -> Open Perspective, и вы должны найти его там.
Ричард
4
В качестве альтернативы вы можете перейти к «Варианты разработки» и установить фоновый предел «без фоновых процессов», тогда каждый раз, когда вы нажимаете домой, процесс умирает.
Жоао Гаваззи
56

Кажется, это работает для меня:

adb shell am kill <package_name>

Это отличается от adb shell killупомянутого ФП.

Обратите внимание, что справка для am killкоманды говорит:

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

Таким образом, он не убьет процесс, если он находится на переднем плане. Кажется, это работает так, как того хотел ОП, потому что, если я отойду от своего приложения, а затем запустлю adb shell am kill <package_name>его, приложение будет убито (я подтвердил это, используя psустройство). Затем, если я вернусь в приложение, то вернусь к деятельности, в которой я был ранее - то есть в примере с OP процесс воссоздается и создается ActivityD (а не ActivityC, как кажется, срабатывают большинство других методов убийства).

Извините, я опоздал на пару лет на OP, но, надеюсь, другие найдут это полезным.

HexAndBugs
источник
Спасибо! это то, что я искал! Я хочу указать, что эта команда ведет себя иначе, чем adb shell am force-stop. Последний также удалит все отложенные объекты, связанные с вашим приложением (например, уведомления), а первый - нет.
Бонниз
Указывает на любого, кто исследует исходный код ОС, чтобы увидеть, какой код он запускает, когда он убивает процесс для восстановления памяти - моя ставка - это тот же код, что и am kill.
androidguy
не работает на Android 7.1 Samsung J5. psпоказывает мое приложение
Valgaal
17

Другой метод, вероятно, сценарий, так как он не требует DDMS:

Однократная настройка: перейдите в «Параметры разработчика», выберите «Настройка предела фоновых процессов», измените значение со «Стандартный лимит» на «Без фоновых процессов».

Когда вам нужно перезапустить процесс, нажмите кнопку домой. Процесс будет убит (вы можете проверить в logcat / Android Monitor в студии - процесс будет помечен [DEAD]). Затем вернитесь в приложение с помощью переключателя задач.

Мерк
источник
2
Интересный. Я думаю, что это не влияет на Foreground Services, верно?
Игорь Ганапольский
Мне нужно сделать это на реальных устройствах под управлением Android, начиная с версии 2.3.3, так что это не поможет.
Дэвид Вассер
13

Этот вопрос старый, но есть ответ на этот вопрос, который не требует adb, Android Studio и т. Д. Единственное требование - API 23 или более поздняя версия.

Чтобы имитировать перезапуск приложения ОС, перейдите в настройки приложения во время его работы, отключите (затем вы можете включить) разрешение и верните приложение из последних приложений. Когда разрешение отключено, ОС убивает приложение, но сохраняет сохраненные состояния экземпляра. Когда пользователь возвращает приложение, приложение и последнее действие (с сохраненным состоянием) воссоздаются.

Метод «без фоновых процессов» иногда вызывает такое же поведение, но не всегда. Например, если приложение запускает фоновую службу, «Нет фоновых процессов» ничего не делает. Но приложение может быть убито системой, включая ее сервисы. Метод разрешения работает, даже если приложение имеет сервис.

Пример:

Наше приложение имеет два вида деятельности. ActivityA - это основное действие, которое запускается с панели запуска. ActivityB запускается из ActivityA. Я покажу только методы onCreate, onStart, onStop, onDestroy. Android всегда вызывает onSaveInstanceState перед вызовом onStop, поскольку активность, находящаяся в состоянии остановки, может быть прервана системой. [ https://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle]

Метод разрешения:

<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

Я хочу сравнить другие методы, которые упоминаются в других ответах.

Не сохранять активность: это не убивает приложение.

<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

Метод принудительной остановки: не сохраняет сохраненные состояния экземпляра

<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
источник
~ " приложение может быть убито системой, включая ее службу ". Не
Foreground
@DavidWasser Читайте спецификацию! developer.android.com/guide/components/services.html#Foreground Служба переднего плана - это служба, о которой пользователь активно осведомлен, и которая не является кандидатом в систему для уничтожения при нехватке памяти.
Игорь Ганапольский
3
@IgorGanapolsky Документация хороша, особенно если она полная и правильная (чего, к сожалению, нет), но я обычно больше полагаюсь на реальные личные наблюдения. Я видел передний план, Serviceубитый много раз. Даже если в системе недостаточно памяти. Большинство производителей устройств написали свои собственные «оптимизации» и «улучшения» для ОС Android с целью экономии заряда батареи. Многие устройства имеют гораздо более агрессивных «убийц», чем стандартный Android.
Дэвид Вассер
@DavidWasser Ярмарка наблюдения. Итак, после всех этих лет, вы пришли к решению?
Игорь Ганапольский
@IgorGanapolsky Нет. Я не нашел решения этой проблемы. Вот почему вопрос все еще открыт.
Дэвид Вассер
7

Я очень опоздал на вечеринку, и несколько человек до меня дали тот же правильный ответ, но для упрощения для тех, кто придет за мной, просто нажмите кнопку «Домой» и выполните команду:

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

Приложение не потеряет свое состояние, и, по моему собственному опыту, оно работает так же, как ОС убивало приложение в фоновом режиме. Это работает только для отладки встроенных приложений

Hirschen
источник
Я получаю'grep' is not recognized as an internal or external command, operable program or batch file.
Дейл
Но я сделал это, находясь в оболочке run-as <package name> kill 7379, но это поставило меня на предыдущее действие, а не на то, на котором я был, когда нажимал кнопку «Домой».
Дейл
6

Вот как вы делаете это в Android Studio.

  1. Подключите ваше устройство в режиме отладки к компьютеру.
  2. Откройте приложение на своем устройстве и перейдите к любой активности, которую вы хотите проверить «Вернись из мертвых».
  3. Нажмите кнопку «Домой» на вашем устройстве.
  4. В Android Studio перейдите в Android Monitor -> Monitors и нажмите значок «Завершение работы приложения».
  5. Теперь вы можете либо вернуться к своему приложению через последние приложения, либо щелкнув по значку его запуска, в моих тестах поведение было таким же.
Дбар
источник
2
Это не поможет. Мне нужно сделать это программно, в комплекте тестов. Но все равно спасибо.
Дэвид Вассер
Кроме того, этот ответ в значительной степени совпадает с ответом Марка.
Дэвид Вассер
Есть идеи, как это сделать с помощью UIAutomator или Espresso?
Игорь Ганапольский
Я не могу найти Android Monitor - Monitors. Должно быть что-то, от чего они избавились. Я на v 3.2.1
Дейл
1
@Dale Android Device Monitor устарел в Android Studio 3.1 и удален из Android Studio 3.2.
Valgaal
5

Поместите приложение в фоновом режиме с помощью кнопки HOME

Выберите свой процесс в режиме «Logcat» в Android Studio, затем нажмите «Завершить приложение» в нижнем левом углу.

кнопка завершения

Теперь запустите ваше приложение из панели запуска на устройстве Android


РЕДАКТИРОВАТЬ: Согласно Интернету, следующее также работает:

 adb shell am kill [my-package-name]

РЕДАКТИРОВАТЬ из будущего: кое-что отметить, в Android Studio 4.0 произошли изменения, если вы используете Runиз AS, то TerminateвыдастForce Stop .

Тем не менее, если вы запустите его после запуска, а затем попытаетесь смоделировать его таким образом, вы получите желаемый результат (нехватка памяти).

EpicPandaForce
источник
Это двойной ответ
Оник
@Onik У всех остальных есть куча ненужных пухов, таких как ddms и все такое. Хотя технически да, stackoverflow.com/a/41975750/2413303 говорит то же самое. Может быть, я должен добавить фотографии.
EpicPandaForce
Это не полезно У меня есть тестовый жгут, и я не могу выполнить эти действия из тестового жгута. Кроме того, поведение не совпадает с тем, что происходит, когда Android убивает процесс.
Дэвид Вассер
the behaviour is not the same as what happens when Android kills the processда, это так
EpicPandaForce
Этот процесс показывает мне предыдущую активность, а не активность, когда нажата кнопка домой.
Дейл
2

Вы можете сделать следующие шаги для воспроизведения искомого поведения:

  1. Откройте свое приложение, перейдите к началу активности
  2. Используйте панель уведомлений для перехода к любому другому полноэкранному приложению (например, к системным настройкам - в правом верхнем углу)
  3. Убей свой процесс подачи заявления
  4. Нажмите кнопку назад
Игорь Костомин
источник
1
Спасибо за ваш ответ, но он мне не нужен, потому что мне нужен автоматизированный способ сделать это для автоматизированного тестирования.
Дэвид Вассер
Итак, это единственный метод, который, как я обнаружил, на самом деле имитирует очистку памяти Android и уничтожение вашего приложения (мой уровень API равен 19, и поэтому я не могу использовать команду send-trim-memory). Другие команды, такие как adb shell am force-stop com.my.app.package или kill, не будут воспроизводить тот же точный процесс, который будет выполнен в соответствии с вышеописанной процедурой!
Марк Гарсия
2

В параметрах разработчика в разделе «Настройки» выберите «Не сохранять действия», что приведет к уничтожению действий, как только вы отойдете от них.

Примечание. Согласно приведенному ниже полезному комментарию, используйте его только в том случае, если вам не нужны очищаемые статические значения.

MSpeed
источник
По сути, это то же самое, что решение, уже опубликованное и отклоненное год назад. Похоже, единственное отличие состоит в том, что приложение используется для его установки на эмуляторе по сравнению с более поздними телефонами, которые его поддерживают.
Крис Страттон
1
Извините, как говорит Крис Страттон, это почти то же самое предложение, что и другой ответ. Это не о деятельности по отделке Android. Речь идет о том, что Android убивает весь процесс (что он делает довольно регулярно и эффективно, особенно на устройствах HTC под управлением Android 4.x).
Дэвид Вассер
6
Это почти хорошо, но это не убьет процесс, а только разрушит активность. Что это означает? Ваша активность будет открыта с помощью saveInstanceState, но все статические переменные все еще находятся в процессе. После уничтожения процесса все статические переменные также очищаются.
Mark
1

Нажмите кнопку «Домой» и сначала поместите приложение в фоновом режиме. Затем остановите или убейте процесс из DDMS или ADB.

Monstieur
источник
Спасибо за ваш ответ, но он мне не нужен, потому что мне нужен автоматизированный способ сделать это для автоматизированного тестирования.
Дэвид Вассер
Android никогда не убьет ваш процесс, если ваша активность сейчас находится на переднем плане. Вы пытаетесь проверить состояние, которое никогда не произойдет. Если ваша деятельность находится в фоновом режиме, то ее состояние уже сохранено, и нет никакой разницы между тем, как вы убили ее вручную, и Android убила ее при нехватке памяти, то есть процесс просто убит; нет ничего особенного в уничтожении памяти. Когда память снова станет доступной, ваши прикрепленные сервисы будут перезапущены (кроме версии 4.4), а когда вы нажмете на значок или недавнее задание, состояние стека и активности будет восстановлено.
Месье
3
В вашем примере он возвращается к действию C, потому что вы убили процесс, когда действие D было видно на экране (это никогда не произойдет даже при нехватке памяти), а состояние действия C было сохранено, поскольку оно находится в фоновом режиме. Ваш процесс будет убит только в том случае, если он вообще не находится на переднем плане, т. Е. Состояние Деятельности D было бы сохранено при переходе в фоновый режим и, таким образом, будет восстановлено, даже если ваш процесс будет убит. Чтобы выполнить свои тесты для каждого действия, вы ДОЛЖНЫ отправить приложение в фоновый режим, прежде чем убить его.
Monstieur
Что вы имели в виду под ~ " и сначала поместили приложение в фоновом режиме "?
Игорь Ганапольский
1

Вы также можете подключиться к своему устройству / эмулятору через терминал adb shell, затем получить PID вашего процесса ps | grep <your_package_nameи выполнить kill -9 <pid>. Затем откройте свернутое приложение из средства выбора последних приложений, и оно перезапустит последнее действие

qbasso
источник
Это так же, как Android убивает процесс в условиях нехватки памяти? Я думаю, что ОП специально хотел этого ...
Игорь Ганапольский
1
@IgorGanapolsky теоретически, хотя вы не могли бы сделать это на реальном устройстве, если оно не имеет рута.
Фран Марзоа
0

Корень вашей проблемы, кажется, в том, что ваш Activity находитесь на переднем плане, когда вы убиваете процесс.

Вы можете наблюдать это, нажав Стоп в DDMS, когда Activity он виден (происходит именно то, что вы описали) и сравнив это с нажатием «Стоп» после возвращения домой и последующего возврата в приложение.

Просто убедитесь, что moveTaskToBack(true)как-то в ваших тестах.

MaciejGórski
источник
0

Я не уверен, что это ответ, который вы ищете, это больше похоже на логическую мысль.

Я не думаю, что вы действительно можете сделать полностью автоматизированный тест, единственный способ имитировать его, это будет воссоздать его, у АКА так много действий, что Android убьет ваше приложение.

Поэтому моя идея или предложение - сделать еще одно небольшое приложение, которое будет отображать новые действия, пока Android не исчерпает память и не начнет обрабатывать его в фоновом режиме.

Что-то среди линии:

Запустить действие i -> Проверить запущенный процесс, если приложение находится в списке, увеличить i и перезапустить цикл, не закрывая текущее действие, иначе -> уменьшить i и закрыть текущее действие, вернуться к предыдущему и перепроверить ...

Эмиль Боркони
источник
0

Когда процесс приложения умирает, Android просматривает записи действий (записи представляют действия в стеке истории) и решает какие из них оставить в истории, а какие удалить из нее.

Одним из ключевых моментов здесь является ActivityRecordполе под названием haveState, которое инженеры Android Framework описывают как «мы получили последнее состояние активности?».

По умолчанию Android считает, что активность имеет состояние. Действие становится не сохраняющим состояние, когда приложение сообщает службе диспетчера задач о том, что действие возобновлено, и это действует, пока приложение не уведомит платформу о том, что действие перешло в состояние Остановлено. Проще говоря, haveStateзначение falseмежду активностью onResume()вызывается и onStop()или onSaveInstanceState()вызывается в зависимости от целевой версии приложения.

Если я убью процесс, Android воссоздает процесс и создает ActivityC.

В этом случае ActivityD не имеет android:stateNotNeeded="true"атрибута в манифесте приложения, и в настоящее время он работает на переднем плане, поэтому Android удаляет его из истории, так как система не получила свое последнее состояние.

Как симулировать Android, убивающий мой процесс

Как уже упоминалось несколько раз, вы можете просто переместить приложение в фоновый режим, чтобы верхняя активность в заднем стеке активности сохранила свое состояние, и после этого вы можете завершить процесс приложения через Android Debug Bridge, Android Studio или используя Свойство «Фоновые процессы» в параметрах разработчика. После этого ваша недавняя активность будет успешно воссоздана.

Несмотря на это, есть еще один простой способ проверить сценарий смерти процесса приложения. Зная все вышеописанное и тот факт, что если вы запустите новый ActivityE из текущего запущенного ActivityD, то onStop()обратный вызов ActivityD вызывается только после onResume()метода ActivityE , вы можете выполнить следующий трюк.

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

Тогда просто начни TerminatorActivity когда хотите убить приложение.

Наконец, есть легкий инструмент, который упрощает тестирование смерти вашего приложения, называется Venom .

ivkil
источник