В своем последнем блоге я говорил о том, как разработчики могут использовать сопрограммы Kotlin для эффективного решения длительных задач в своих приложениях:



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

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

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

Войдите в службы переднего плана

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



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

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

В AfterShoot мы перенесем обработку машинного обучения, которая происходит в нашем приложении, в службу переднего плана. Это даст нам 2 преимущества:

  1. Обработка продолжится, даже если приложение остановлено.
  2. Пользователь может просматривать уже обработанные изображения, не дожидаясь завершения процесса.

Реализация службы переднего плана в AfterShoot

Вот как выглядит текущий код:

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

Шаг 1. Создайте новую службу и зарегистрируйте ее в AndroidManifest.

Сначала нам нужно создать новый собственный класс и заставить его расширять Service базовый класс. После этого нам нужно предоставить реализацию для методов onCreate() и onBind() из базового класса. Вот как это выглядит:

После этого вам необходимо зарегистрировать эту службу в теге <application> вашего AndroidManifest.xml и также запросить разрешение на отображение службы переднего плана:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<application>
...
<service android:name=".service.ModelRunnerService" />
...
</application>

Шаг 2. Перенесите код из активности в службу

Когда у нас есть служба, нам нужно перенести наш код для обработки изображений из Activity в службу. Для этого мы будем использовать метод onCreate().

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

Вот как выглядит код:

Если вы попытаетесь запустить службу, которую мы создали до сих пор, она будет работать нормально, но как только приложение будет закрыто, служба также будет остановлена. Нам еще предстоит преобразовать его в службу переднего плана.

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

Шаг 3: Создание уведомления для нашей службы переднего плана

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

Прежде чем создавать уведомление, нам нужно создать канал, в котором мы хотим отображать уведомление. Начиная с Android Oreo, Android не позволяет отображать уведомления, которые не принадлежат каналу. Распределяя уведомления по каналам, пользователи могут отключать определенные каналы уведомлений для вашего приложения вместо отключения всех уведомлений. Вы можете узнать больше о преимуществах наличия каналов уведомлений здесь:



Вот как можно создать канал уведомлений:

После этого мы можем продолжить и сделать наше уведомление:

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

Шаг 4: Запуск службы переднего плана

Чтобы запустить службу в режиме переднего плана, необходимо сделать 2 вещи:

  1. Начните ModelRunnerService с нашей деятельности.
  2. В методе onCreate ModelRunnerService продвиньте его в службу переднего плана.

Первый шаг относительно прост и может быть выполнен следующим образом:

На втором этапе нам нужно сделать две вещи в нашем onCreate() методе ModelRunnerService: создать канал уведомлений и связать уведомление со службой:

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

Вот и все! При запуске приложения я вижу следующее уведомление:

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

Шаг 5. (Необязательно) Обновление индикатора выполнения в уведомлении

Обновление может быть неправильным словом здесь, так как вам нужно создавать новое уведомление каждый раз, когда вы хотите обновить существующее уведомление. Итак, давайте сделаем это:

Вот и все! Запустив приложение сейчас, вы должны увидеть следующее уведомление:

Исходный код кода, описанного в этом сообщении блога, можно найти здесь:



В частности, соответствующий исходный код можно найти в ModelRunnerService.kt файле.

Спасибо за внимание! Если вам понравилась эта история, пожалуйста, нажмите 👏 кнопку и поделитесь ею, чтобы помочь другим найти ее! Не стесняйтесь оставлять комментарии 💬 ниже.

Есть отзывы? Подключим в Twitter.

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

Являясь независимой редакцией, Heartbeat спонсируется и публикуется Comet, платформой MLOps, которая позволяет специалистам по данным и группам машинного обучения отслеживать, сравнивать, объяснять и оптимизировать свои эксперименты. Мы платим участникам и не продаем рекламу.

Если вы хотите внести свой вклад, отправляйтесь на наш призыв к участникам. Вы также можете подписаться на наши еженедельные информационные бюллетени (Deep Learning Weekly и Comet Newsletter), присоединиться к нам в » «Slack и подписаться на Comet в Twitter и LinkedIn для получения ресурсов, событий и гораздо больше, что поможет вам быстрее и лучше строить лучшие модели машинного обучения.