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

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

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

При гуглении нативных скриптов и фоновых сервисов на первое место выходит отличный туториал и репозиторий с примерами (Я говорю об этом).

Увы, здесь используется IntentService, который запускается только по расписанию и завершает работу после завершения своих задач. Однако создать непрерывный фоновый сервис довольно просто, просто не хватает примеров по этой теме (что и призвана исправить эта статья).

Полное репозиторий с рабочими примерами вы можете найти здесь.

В этой статье я предполагаю, что мы работаем с машинописным шаблоном hello_world: tns create ServiceExample --ts --appid tk.ozymandias.ServiceExample

Не должно быть сложно адаптироваться к другим шаблонам/технологиям.

Сначала создайте новую подпапку в app/, назовите ее service. Это делается исключительно для того, чтобы поддерживать структуру вашего проекта в чистоте и порядке. Теперь создайте новый файл под app/service/continuous_service.android.ts с этим содержимым

Теперь это очень простой сервис, он просто работает в фоновом режиме и каждую секунду выводит «PING» на консоль.

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

Здесь очевиден первый: аннотация @JavaProxy. Использование переменной здесь вызовет ошибки о существующих расширениях, и вместо значения переменной оно будет неопределенным.

Второй будет в манифесте. Подробнее об этом позже.

onCreate вызывается один раз при создании экземпляра службы, onStartCommand вызывается при каждом запуске службы, а onDestroy вызывается при выходе из службы.

То, как служба запускается и перезапускается, зависит от того, что вы возвращаете из onStartCommand. У вас может возникнуть соблазн вернуть START_STICKY здесь, но это приведет к сбоям, когда ваше приложение будет уничтожено, потому что система попытается перезапустить вашу службу с намерением null.

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

Начнем с создания широковещательного приемника.

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

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

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

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

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

Нам нужно еще раз изменить наш сервис, на этот раз метод onCreate:

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

Теперь, если вы попробуете код до сих пор, он рухнет. Это потому, что мы ничего не объявляли в AndroidManifest.xml! Что нам нужно объявить, так это необходимые нам разрешения (только в последних версиях Android), службу и получателя.

Без лишних слов, вот манифест:

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

Возможно, вам придется declare const com: any; где-то в верхней части файла, иначе машинописный текст может вызвать подозрение.

Итак, что мы здесь сделали?

Мы создали отложенное намерение, указывающее на основное действие нашего приложения, поэтому теперь при нажатии на уведомление открывается ваше приложение. Что касается параметров уведомлений, то важными битами являются setContentText и setSmallIcon. Если оба из них отсутствуют как минимум, вы все равно получите общее уведомление.

Это была моя первая статья, будьте добры.

Первоначально опубликовано наhttps://dev.to/ozymandiasthegreat/android-continuous-background-services-with-nativescript-42c9.