Время жизни приложения Android: может быть, оно бесконечно? - PullRequest
4 голосов
/ 22 декабря 2011

Я разрабатываю приложение, предназначенное для превращения телефона Android в удаленное устройство, работающее без действий пользователя.В то время, когда приложение создается Activity, которая устанавливает AlarmManager для выполнения службы (класс внутри проекта) каждые X минут.

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

Я не думаю, что это связано с ошибкой, потому что отладка prew не дает мне никаких ошибок.

Таким образом, я должен предположить, что android убил активность (системе нужно больше памяти?), И, как показывает изображение, нет способа сделать резервную копию.

Flow diagram

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

В моем случае система может уничтожить мой график обслуживания диспетчера тревог?

Что я могу сделать, чтобы служба запускала Alarm Manager навсегда?

( ВНИМАНИЕ : в настоящее время еще существует WAKE LOCK, ноэто касается только выполнения службы! Я надеюсь, что вы понимаете, что служба вызывается диспетчером аварийных сигналов каждые х минут, а затем завершается ... я хочу выполнять эти операции в течение неопределенного времени)

[Я не опубликовал исходный код, потому что он слишком длинный]

Ответы [ 3 ]

4 голосов
/ 11 января 2012

Lork,

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

CommonsWare правильно, что вам нужно использовать AlarmManager, но я подозреваю, что вы уже знали это. Сначала пара комментариев, чтобы убедиться, что все понятно. Тревоги, созданные AlarmManager, существуют на системном уровне, то есть они могут существовать вне жизненного цикла действия и приложения, которое их создало. Если вы установили будильник, но не хотите, чтобы он выключался, если ваше приложение меняет состояние (например, после того, как оно было уничтожено), вы можете отменить его, используя alarmManager.cancel (pendingIntent) - просто создайте намерения и менеджер тревог с те же параметры и Android будут соответствовать сигнализации). Аналогично, BroadcastReceivers регистрируются на системном уровне (по крайней мере, если они объявлены в manifest.xml) и могут существовать за пределами жизненного цикла действия и приложения, которое их создало. Опять же, если вы хотите убедиться, что BroadcastReceiver не сработает в ответ на событие, произошедшее после того, как ваше приложение изменило состояние (например, после того, как оно было уничтожено), вам необходимо явно отменить регистрацию в этом коде. Если это было зарегистрировано программно, тогда используйте context.unregisterReceiver (broadcastReceiver); если он был статически зарегистрирован в Манифесте, это не так просто - вам нужно будет извлечь получателя с помощью PackageManager и ComponentName (см .: Android - как отменить регистрацию получателя, созданного в манифесте? ) - и помните, что вам нужно будет снова включить приемник, если он вам понадобится снова.

Вы говорите, что уже настроили свой будильник. Убедитесь, что вы указали ELAPSED_REALTIME_WAKEUP или RTC_WAKUP для Типа будильника, чтобы он работал, даже когда телефон находится в «спящем» режиме. Вы также говорите, что уже создали связанный BroadcastReceiver для обработки события тревоги. BroadcastReceiver должен выполнять минимум работы, поэтому вы должны обрабатывать любую обработку в отдельном потоке или путем запуска службы. Вы решили запустить Службу и завершить ее с помощью stopSelf () после ее завершения, чтобы она не использовала системные ресурсы. Пока все хорошо.

Это нормально, когда приложение работает, однако, поскольку вам требуется что-то, что надежно работает в течение неопределенного периода времени, вы должны убедиться, что вы управляете «исключительными» ситуациями, когда оно приостанавливается, устройство «спит», Приложение зависло / завершилось, или устройство перезагрузилось (и любые другие возможные сценарии исключений). Вот те проблемы, на которые я указал, которые вам нужно решить:

Во-первых: WakeLock гарантирован только на время действия метода onReceive () BroadcastReceiver. После его завершения устройство может вернуться в «спящий режим», даже если ваша Служба не запущена или даже не завершена, поэтому вам необходимо создать WakeLock, передать его Службе и освободить ее, прежде чем вы остановите Службу. (Примечание: для вашего приложения вам требуется PARTIAL_WAKE_LOCK). Будьте очень осторожны при использовании WakeLocks - убедитесь, что вы держите WakeLock только в течение минимально необходимого времени и освобождаете его, так как его использование может привести к чрезмерной разрядке батареи). См. http://www.netmite.com/android/mydroid/development/pdk/docs/power_management.html для примера использования WakeLocks.

Второе: если вы сбросите свою тревогу в коде (вместо определения автоматически повторяющейся), сделайте это в методе OnReceive () BroadcastReceiver или в качестве первого шага в запущенной вами службе - это обеспечит Тревога повторяется независимо от состояния приложения или устройства.

В-третьих: убедитесь, что любойИспользуемые вами контексты будут ненулевыми значениями.Вы можете динамически извлекать контекст в Сервисе, используя getApplicationContext ().В противном случае это может быть достигнуто путем ТОЧНОЙ передачи контекста из вашего приложения в сигнализацию и обеспечения его полного прохождения через BroadcastReceiver и связанные потоки и службы.Если у вас есть статически сохраненный контекст в вашем приложении, так что вы можете получить его где угодно, тогда он вернет нулевое значение, если приложение завершено.Если вы используете контекст (например, для извлечения ресурса, доступа к базе данных и т. Д.), И он имеет значение null, это вызовет исключение нулевого указателя, и Service или BroadcastReceiver потерпит крах.Я полагаю, что это наиболее вероятная причина того, что ваши приемники Broadcast не работают, когда ваше приложение завершено.

В-четвертых: вы можете делать ссылки на ResourceID (например, R.drawable.icon) в вашей службе илиBroadcastReceiver полностью определен (. R.drawable.icon) или сгенерирован из переданного контекста.Я еще не нашел, что это необходимо, но я подозреваю, что это может быть разумно.

Пятое: Реализация отдельного BroadcastReceiver для обработки сценария перезагрузки устройства (событие ON_BOOT_COMPLETE).Вы можете получить этот приемник для повторного запуска приложения, если это необходимо, или он может запустить службу, чтобы проверить, что ваше приложение должно быть активным, настроить любые необходимые параметры и настроить соответствующие сигналы тревоги, а затем завершить его с помощью stopSelf (),или просто снова установите будильник и позвольте этому приемнику справиться со всем этим.Не забудьте убедиться, что у службы есть WakeLock в течение всего срока ее действия, а также после ее завершения.Если вы не просто перезапустите приложение или Службу (объявленную как часть вашего приложения), то вам также следует статически хранить правильный контекст в качестве атрибута класса в BroadcastReceiver, если он вам нужен, чтобы он был доступен для доступа.resources.

Несколько других вещей, которые вы можете рассмотреть: поскольку ваша установка удаленная, я бы серьезно подумал о сохранении любых постоянных данных в таблицах базы данных SQLite.Это обеспечит возможность восстановления данных между завершениями приложений и перезагрузками устройства без необходимости их регенерации.Если ваше приложение взаимодействует с серверной службой, рассмотрите возможность использования push-уведомлений для инициируемой сервером связи, а не периодически проводите опрос приложения.Push-уведомления также можно использовать для «пробуждения и запуска служб и приложений», поэтому их можно использовать как часть удаленного механизма для запроса состояния устройства и вашего приложения.Этот подход также более энергоэффективен и своевременен.Публикуйте информацию в LogCat в ключевых точках вашего кода для отладки.Если приложение завершается, то adb прекращает отслеживать исходный код, выполняемый для получателя и службы, но LogCat продолжает функционировать, поэтому его можно использовать для проверки пути через код и значения переменных.

Другие люди могут иметьлучшие способы решения этих проблем или некоторые другие указания (я, конечно, был бы очень рад увидеть другие отзывы), но я надеюсь, что эти идеи полезны и удачи!

0 голосов
/ 03 января 2012

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

Например: приложение устанавливаетсигнал тревоги с типом ELAPSED_REALTIME_WAKEUP или RTC_WAKUP и имеет широковещательный приемник, который обрабатывает его при запуске через намерение, которое ссылается на контекст приложения и класс приемника широковещательной передачи.Получатель объявлен как в манифесте приложения.

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

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

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

0 голосов
/ 23 декабря 2011

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

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

...