Как узнать активити приложения

Обновлено: 19.09.2024

есть ли простой способ определить, активна ли определенная активность? Я хочу делать определенные вещи, в зависимости от активности действующих. например:

можно использовать static переменная в пределах действия.

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

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

Я думаю, более ясно, что:

гораздо лучший способ, чем использовать статическую переменную и следовать ООП

Shared Preferences может использоваться для обмена переменными с другими activities и услуги от одного application

использовать общие предпочтения. Он имеет самую надежную информацию о состоянии, меньше проблем с переключением/уничтожением приложений, экономит нас, чтобы попросить еще одно разрешение, и дает нам больше контроля, чтобы решить, когда наша деятельность на самом деле является самой верхней. см.подробности здесь abd здесь

опция без использования какой-либо вспомогательной переменной:

где активность f.e.: это или getActivity ().

значение, возвращаемое этим выражением, изменяется в onStart () / onStop (), которые являются событиями, которые запускают / останавливают отображение макета действия на телефоне.

Я MyActivity.класс и метод getCanonicalName, и я получил ответ.

Постоянные Изменения.Пакет и константы.BACKGROUND_SERVICE_CLASS соответственно

спасибо kkudi! Я смог адаптировать ваш ответ к работе для деятельности. вот что сработало в моем приложении..

этот пример даст вам true или false, если topActivity соответствует тому, что делает пользователь. Поэтому, если активность, которую вы проверяете, не отображается (т. е. onPause), вы не получите совпадения. Кроме того, для этого необходимо добавить разрешение в манифест..

Я надеюсь, это было полезно!

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

и переопределить его в производном классе.

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

еще лучше, используйте шину событий, как GreenRobot-х, площади, но это устарело и предлагает использовать RxJava

есть гораздо более простой способ, чем все, что выше, и этот подход не требует использования android.permission.GET_TASKS в манифесте, или имеют вопрос условий гонки или утечек памяти, указанных в принятом ответе.

сделайте статическую переменную в основной деятельности. Static позволяет другим действиям получать данные от другого действия. onPause() установить эту переменную false, onResume и onCreate() установить эту переменную правда.

назначить геттеры и сеттеры этой переменной.

а затем от другой деятельности или службы

Не уверен, что это "правильный" способ "делать вещи".
Если нет способа API решить (или a) вопрос, чем вы должны немного подумать, возможно, вы делаете что-то неправильно и читаете больше документов и т. д.
(Как я понял, статические переменные обычно неверны в android. Конечно, это может сработать, но обязательно будут случаи, когда это не сработает[например, в производстве, на миллионах устройств]).
Именно в вашем случае я предлагаю подумать Почему вы нужно знать, жива ли другая активность. вы можете запустить другое действие для результата, чтобы получить его функциональность. Или вы можете наследовать класс, чтобы получить его функциональность и так далее.
с уважением.

Если вас интересует состояние жизненного цикла конкретного экземпляра activity, решение siliconeagle выглядит правильным, за исключением того, что новая "активная" переменная должна быть переменной экземпляра, а не статической.

в вашей деятельности зарегистрируйте приемник в onStart, отмените регистрацию в onStop. Теперь, когда, например, службе необходимо обработать что-то, что действие может сделать лучше, отправьте заказанную трансляцию из службы (с обработчиком по умолчанию в самой службе). Теперь вы можете отвечать в действии, когда оно выполняется. Служба может проверить результат данных, чтобы увидеть, если трансляция была обработана, и если не принять соответствующие меры.

я использовал проверки if (!a.isFinishing()) и, кажется, делает то, что мне нужно. a экземпляр активности. Это неправильно? Почему никто не попробовал?

кроме принято отвечать, Если у вас есть несколько экземпляров действия, вы можете использовать счетчик вместо:

ActivityLifecycleCallbacks-отличный способ отслеживать все действия в приложении:

расширьте класс приложения и укажите его ссылку в файле манифеста Android:

Ckeck в любом месте от активности для статуса другой деятельности:

найдено простое обходное решение со следующим кодом

эта работа, если у вас нет такой же активности на переднем плане. Если вы открываете из уведомления не работает, я внес некоторые коррективы и пришел с этим:

это работает для меня с несколькими активностями, открытыми одновременно.

Activities являются основной частью разработки приложений для Android. И невозможно понять Activity, не понимая их жизненных циклов. В этом материале вы узнаете всё о жизненном цикле Activity.

Жизненный цикл Activity

Activity — это отдельный экран в Android. Это как окно в приложении для рабочего стола, или фрейм в программе на Java. Activity позволяет вам разместить все ваши компоненты пользовательского интерфейса или виджеты на этом экране.

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

Методы жизненного цикла

Давайте поближе рассмотрим жизненный цикл Android Activity. Каждый раз, когда Activity меняет стадию, вызывается один из следующих методов жизненного цикла для класса Activity.

onCreate() : вызывается при первой инициализации Activity. Вам нужно выполнить этот метод для выполнения любой инициализации в вашей Activity.

onStart() : это вызывается, когда Activity готова и отображается пользователю в первый раз, так как Activity готовится стать интерактивной и выйти на передний план. После завершения вызова этого метода, будет вызван метод onResume() .

onResume() : когда Activity переходит в это состояние, оно начинает взаимодействовать с пользователем. Activity продолжает работать в этом состоянии, пока что-либо не уведёт фокус от приложения или Activity (например, входящий звонок). Если это произойдет, будет вызван метод onPause() .

onPause() : этот метод используется для приостановки действий, которые не должны происходить, пока Activity в стадии паузы. Вызов этого метода указывает на то, что пользователь покинул приложение. К примеру, входящий звонок может перевести, проигрыватель музыки в состояние паузы. Это должно заглушить или остановить воспроизводящуюся музыку. Когда пользователь возвращается в приложение, вызывается метод onResume() .

onStop() : этот метод вызывается, если Activity больше не видна в приложении. Такое может случится, если подгружена другая Activity и она занимает весь экран устройства. Когда вызывает этот метод, Activity сообщается перейти в состояние прекращения работы. В этом состоянии, система либо вызывает onRestart() для возврата взаимодействия с Activity, либо вызывает метод onDestroy() , чтобу убрать Activity.

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

onRestart() : это вызывается, если Activity перезапускается, после того, как было остановлено.

Запуск Activity

Большинство пользовательских взаимодействий приводит к изменению действующей Activity. Поэтому приложение моно раз переключается между Activity за свой жизненный цикл.

Необходимо связывать Activity друг с другом, если требуется чтобы одна Activity запускала другую. Для запуска Activity используйте либо startActivity() , либо startActivityForResult() . В обоих случаях вы должны передать Intent.

Запуск Activity без ожидаемого результата

startActivity() используется, если недавно запущенная Activity не должна возвращать результат.

Следующий фрагмент кода показывает как начать другую Activity, используя этот метод:

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

Запуск Activity на результат

startActivityForResult() используется для запуска другой Activity и ожидает получение данных в эту свежезапущенную Activity. Другими словами, используйте это если хотите получить результат от целевой Activity в вызывающую Activity, т.е. если целевая Activity собирает некую пользовательскую информацию в модальном диалоговом окне.

Вы получите результат от Activity в методе onActivityResult(int requestCode, int resultCode, Intent data) . Результат будет возвращен в качестве Intent.

Пример запуска Activity

Вот пример, который показывает как работает запуск Activity

Во-первых создайте MainActivity с вашим методом onCreate() , файл макета и кодом запроса.

В методе onCreate() вы создадите новый экземпляр намерения (intent) чтобы запустить вторую Activity.

Когда будете готовы к запуску Activity, скажем в ответ на нажатие кнопки, вы вызовете startActivityForResult() , которая передаст свеже-созданное намерение и код запроса.

В вашей MainActivity , вам всё же нужно обработать результат событий Activity. Это выполняется путём реализации метода onActivityResult() . Вот так вы и получите результат от другой Activity.

Вот как это должно выглядеть:

Теперь создайте вашу SecondActivity . Это должно быть похоже на код ниже.

Прерывание Activity

Перед завершением Activity, будут вызваны соответствующие методы жизненного цикла.

Метод onPause() должен остановить все "слушания" и обновления интерфейса. Метод onStop() должен сохранять данные приложения. И наконец, метод onDestroy() высвободит любые ресурсы, выделенные для Activity.

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

Стадия экземпляра Activity

Activity нужен способ сохранить полезное состояние и пользовательские данные, которые она получила. Эти данные могут быть получены от пользователя или созданы, пока Activity не отображалась на экране.

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

Чтобы сохранить состояние Activity, можно переопределить метод onSaveInstanceState(). Этот метод передаёт объект Bundle , в качестве параметра. Пакет (bundle) может содержать строки, простые типы данных или объекты. В этом методе, просто добавьте любые данные о важном состоянии в пакет (bundle). Позже этот пакет вернется Activity, так что вы сможете восстановить состояние Activity.

Чтобы извлечь сохраненное состояние из пакета и восстановить его (состояние), примените метод onRestoreInstanceState() . Этот callback вызывается между методами жизненного цикла onStart() и onResume() .

Мы получше рассмотрим состояние экземпляра Activity в будущих статьях.

Заключение

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

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

Activity - это компонент приложения, который является одним из его фундаментальных строительных блоков. Его основное предназначение заключается в том, что оно служит точкой входа для взаимодействия приложения с пользователем, а также отвечает за то, как пользователь перемещается внутри приложения или между приложениями.

По сути активити - это окно, с которым пользователь взаимодействует. И в этом окне можно “нарисовать” какой-либо пользовательский интерфейс. При этом окно может быть как на весь экран, так и занимать только заданную его часть и даже размещаться поверх других окон. Но, как правило, каждая активити реализует только одно окно. Однако, у каждого приложения может быть несколько активити.

Одна из активити может быть отмечена как основная (или главная) и тогда она будет появляться первой при запуске приложения. А уже из нее можно запустить другие активити. Причем не только те, которые принадлежат текущему приложению, но и активити из других приложений. Может показаться, что все запускаемые активити являются частями одного приложения. На самом же деле они могут быть определены в разных приложениях и работать в разных процессах.

Мне встречалась интересная аналогия, что активити - это как страницы разных сайтов, открываемых в браузере по ссылке.

Создание новой активити

Первым делом следует создать новый класс, который должен наследоваться от класса Activity и переопределять метод onCreate() .

Таким образом мы получим пустое окно. Чтобы в этом окне хоть что-то отображалось нужно создать разметку - layout. Добавляется она в ресурсах (папка res) в подпапке layout.

Теперь эту разметку нужно “подключить” к активити в методе onCreate() .

Ну и наконец, чтобы приложение могло использовать только что созданную активити её нужно объявить в манифесте приложения при помощи элемента <activity> .

Все вышеперечисленные шаги можно сократить до одного благодаря наличию в Android Studio стандартных шаблонов: правой кнопкой мыши вызываем контекстное меню и в нём выбираем New -> Activity -> “выбрать нужный шаблон”.

new-activity-patterns

После этого класс, разметка, а также запись в манифесте будут добавлены автоматически.

Настройка активити в манифесте

Рассмотрим более подробно, какую информацию нужно или можно добавлять для элемента <activity> в манифесте.

Объявление активити

Как упоминалось выше, все активити приложения обязательно нужно регистрировать в манифесте. Для этого предназначен элемент <activity> . У этого элемента есть только один обязательный атрибут - android:name , который ссылается на имя класса активити.

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

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

Объявление intent-фильтров

Скорее всего данная тема будет не особо понятна новичкам, поэтому сначала рекомендую ознакомиться с тем, что такое Intent.

Intent-фильтры - это выражение в файле манифеста, которое указывает, какие объекты Intent может обработать текущее приложение. Т.е. они позволяют настроить, на что будет реагировать активити.

Если у приложения отсутствуют intent-фильтры, то запустить его можно будет только с помощью явного Intent (по имени класса).

Объявляются они при помощи атрибута <intent-filter> внутри элемента <activity> . При этом <intent-filter> должен обязательно содержать атрибут <action> и может содержать необязательные атрибуты <category> и <data> . Все вместе эти элементы указывают на тип объекта Intent , на который текущая активити сможет реагировать.

Например, активити можно добавить intent-фильтр, который будет говорить, что она умеет отправлять данные.

Выше упоминалось, что активити можно отметить как основную. Это тоже осуществляется с помощью intent-фильтра.

Объявление разрешений

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

Например, ваше приложение хочет использовать приложение SocialApp для публикации какой-либо информации в социальных сетях. Приложение SocialApp должно определить разрешение:

И чтобы иметь возможность запускать SocialApp вы должны в своем приложении добавить идентичное разрешение:

Жизненный цикл

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

Для чего это делать? Чтобы избежать следующих ситуаций:

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

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

Методы обратного вызова

onCreate()

Вызывается при создании и перезапуске активити. После создания активити она переходит в состояние “Создана” (Created state), но существует в нём недолго. Как только метод onCreate() будет выполнен, активити переходит в статус “Запущена” (Started state).

Этому методу передается объект Bundle , который содержит в себе сохранённое состояние активити (если она перезапустилась), либо null (если активити ранее не существовала).

Что в нём должно происходить:
Выполнение базовой логики запуска приложения, которая должна происходить только один раз за весь период действия. Например, создание пользовательского интерфейса, привязка данных, создание сервисов (service) и потоков.

Следующий метод:
onStart() .

onStart()

Вызывается при переходе активити в состояние “Запущена” (Started state), т.е. сразу после выполнения метода onCreate() . В этом состоянии активити существует пока метод onStart() не будет выполнен. После чего переходит в состояние “Возобновлена” (Resumed state).

Также этот метод делает активити видимой для пользователя, но с ней еще нельзя взаимодействовать.

Что в нём должно происходить:
Выполнение кода, который поддерживает пользовательский интерфейс

Следующий метод:
onResume()

onResume()

Вызывается при переходе активити в состояние “Возобновлена” (Resumed state), т.е. сразу после выполнения метода onStart() . В этом состоянии активити взаимодействует с пользователем. И продолжается это до тех пор пока пользователя что-то не отвлечёт, например, телефонный звонок, переход к другому приложению или отключение экрана устройства. В этом случае активити перейдёт в состояние “Приостановлена” (Paused state).

Что в нём должно происходить:
Включение функциональности, которая должна работать, пока активити видна и находится на переднем плане. Возобновить работу того, что было приостановлено в методе onPause() .

Следующий метод:
onPause()

onPause()

Вызывается, когда активити теряет фокус и переходит в состояние “Приостановлена” (Paused state). Т.е. активити больше не находится на переднем плане, хотя может быть всё ещё видна пользователю.

Завершение работы метода onPause() не означает, что активити перейдёт в другое состояние. Она будет оставаться в состоянии “Приостановлена” до тех пор, пока либо не перейдёт обратно на передний план (вызовется метод onResume() ), либо пока полностью не станет невидимой (вызовется метод onStop() ).

Что в нём должно происходить:
Приостановка или настройка операций, которые не должны продолжаться пока активити находится в состоянии “Приостановлена”, но ожидается, что вы вскоре их возобновите.
Код должен быть легковесным, так как нет гарантии, что он успеет выполнится.

Следующий метод:
onResume() или onStop() .

onStop()

Вызывается, когда активити больше не видна пользователю и переходит в состояние “Остановлена”. Это может произойти, если активити уничтожается, запускается новая активити или возобновляет работу существующая активити, закрывая собой текущую. Несмотря на это, активити остаётся в памяти, но она больше не привязана к диспетчеру окон.

Из состояния “Остановлена” активити либо возвращается к взаимодействию с пользователем (вызывается метод onRestart() ), либо завершается (вызывается метод onDestroy() ).

Что в нём должно происходить:
Остановка функций, работа которых не нужна пока активити невидима для пользователя. Например, остановка анимаций, сохранение информации в базе данных.

Следующий метод:
onRestart() или onDestroy() .

onRestart()

Вызывается, когда активити в состоянии “Остановлена” повторно отображается пользователю.

Что в нём должно происходить:
Выполнение действий, которые должны выполняться при повторном запуске активити в рамках одного жизненного цикла приложения.

Следующий метод:
onStart() .

onDestroy()

Вызывается перед тем, как активити будет уничтожена и переходит в состояние “Уничтожена” (Destroyed state). Это происходит в следующих случаях: если пользователь её закрыл; был вызван метод finish() ; произошла смена конфигурации активити (например, поворот экрана). При этом в первых двух случаях метод onDestroy() будет последним обратным вызовом в жизненном цикле активити. Если же onDestroy() был вызван из-за смены конфигурации, система немедленно создаст новый экземпляр активити и вызовет для него метод onCreate() .

Что в нём должно происходить:
Освобождение всех оставшихся ресурсов, которые не были освобождены в методах onPause() и onStop() .
Не использовать для какой-либо критически важной логики, так как данный метод может быть и не вызван.

Следующий метод:
Последний метод жизненного цикла.

Схема состояний активити

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


Android SDK предоставляет много средств для работы с системой. В том числе он позволяет получать список приложений, которые установлены на устройстве. Это может быть полезно, когда нужно получить сведения о сторонних приложениях (размер APK, путь до приложения, имя пакета и т.д.). Например, в наших приложениях получение списка, содержащего сторонние приложения, играет большую роль: в GreenBro с помощью этого списка выводятся сведения о приложениях, а также выполняются различные действия.


В Менеджере системных приложений и APK Extractor же список приложений необходим, чтобы удалять приложения и извлекать APK из приложений соответственно.

В этой статье мы рассмотрим, как можно получать список приложений, установленных на устройстве, а также как происходит установка приложений на устройство.

PackageManager предоставляет API, который фактически управляет установкой, удалением и обновлением приложений. Когда мы устанавливаем файл APK, PackageManager анализирует этот APK и выводит результат.

Получить экземпляр класса PackageManager можно с помощью метода getPackageManager(). PackageManager предоставляет методы для запросов к установленным пакетам и соответствующим разрешениям.

В зависимости от типа данных, на Androiid файлы могут храниться в следующих местах:

  • Предустановленные и системные приложения (Камера, Браузер и т.д.) хранятся в /system/app/
  • Установленные пользователем приложения хранятся в /data/app/
  • PackageManager создаёт каталог /data/data/<имя пакета>/ для хранения базы данных, файлов с предпочтениями, нативных библиотек и кеша.

Менеджер пакетов хранит информацию о приложении в трёх файлах, расположенных в /data/system.

packages.xml

Этот XML-файл содержит список разрешений и пакеты\приложения. Он хранит две вещи: разрешения и пакет. Например:

Тег <package> содержит 10 атрибутов и несколько подтегов.

Атрибут Описание
name Имя пакета
codePath Путь установки APK
nativeLibraryPath Нативная библиотека, расположенная по умолчанию в /data/data/<имя пакета>/lib
flag Хранит флаги ApplicationInfo
ft Время в шестнадцатtричном формате
lt Время установки в шестнадцатеричном формате
ut Время последнего обновления в шестнадцатеричном формате
version Код версии из AndroidManifest.xml
sharedUserId Идентификатор пользователя Linux, который будет использоваться совместно с другими приложениями.
userId Идентификатор пользователя Linux

Подтеги же здесь следующие:

packages.list

Это простой текстовый файл, содержащий имя пакета, идентификатор пользователя, флаги и каталог data.

package-stopped.xml

Рассмотрим получение списка установленных приложений на примере GreenBro.

При запуске приложения запускается AsyncTask, внутри которого получаем экземпляр PackageManager и затем копируем в список List<ApplicationInfo> все данные об установленных приложениях.

Метод getInstalledApplications() принимает в качестве параметра флаг GET_META_DATA, который определяет, что нам нужные метаданные каждого пакета.

Результатом является список объектов ApplicationInfo для каждого установленного приложения. Класс ApplicationInfo предоставляет подробную информацию о пакете, собранную из тега <application> в AndroidManifest.xml, нам оттуда нужны лишь самые важные данные.

Поэтому в цикле проверяем каждый объект из полученного списка и записывать данные в собственный класс AppInfo, чтобы затем использовать в основном потоке.

Здесь с помощью метода getPackageInfo() класса PackageManager мы получаем общую информацию о приложении по заданному имени пакета. После эта информация объединяется с информацией, полученной от getInstalledApplications() и сохраняется в объекте AppInfo со следующими полями:

Чтобы узнать название приложения, можно также воспользоваться PackageManager, как показано ниже.

Проверка же на то, является ли приложение системным, тоже достаточно проста и показана ниже.

В конце работы AsyncTask возвращает результат обратно в основной поток. Вот и всё, мы загрузили себе список всех установленных на устройстве приложений и можем продолжить с ним работу.

Получение списка приложений в Android : 4 комментария

Это локальная переменная, Вы можете передавать контекст из активити или фрагмента

а есть ли функция наподобие getRunnedApplications(), которая выдает список запущенных последних приложений?

как отличить приложение от сервиса? Проверка на системное приложение не помогает

Читайте также: