Задать вопрос
Kavaru
@Kavaru

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

Пишу почтовый клиент на Python, который для заданных контактов показывает переписку в виде чата. Собирается под Android с помощью Buildozer. В активном режиме приложение работает, но хочу ещё запускать фоновую проверку почты отдельным сервисом.

UPDATE: поправил код на использование java-класса. Ошибок не выдает, но сервис в логи ничего не пишет и по внешним признакам - не работает.

В buildozer.spec сервис объявлен так:
services = Chatmail_service:service/background_check.py:foreground:foregroundServiceType=dataSync


Код запуска:
from kivy.utils import platform

    def start_background_service(self):
        if platform != "android":
            logger.info(f"Background service is not started: platform={platform}")
            return

        if self.background_service is not None:
            logger.info("Background service is already started")
            return

        try:
            logger.info(f"Setting up Android background service: platform={platform}")

            from jnius import autoclass

            self.background_activity = autoclass('org.kivy.android.PythonActivity').mActivity
            service_name = f"{self.background_activity.getPackageName()}.ServiceChatmail_service"
            autoclass(service_name).start(self.background_activity, '')
            logger.info("Android background service start requested")

        except Exception as error:
            self.background_activity = None
            logger.exception(f"Unable to start Android background service: {error}")


Код сервиса:
def run():
    logger.info("Foreground mail service started")
    # код проверки
if __name__ == "__main__":
    logger.info("service/background_check.py executed")
    run()


Как правильно запустить сервис, чтобы при свернутом приложении появлялись уведомления о новом сообщении?
  • Вопрос задан
  • 158 просмотров
Подписаться 1 Средний Комментировать
Помогут разобраться в теме Все курсы
  • Академия Эдюсон
    Python-разработчик + ИИ
    9 месяцев
    Далее
  • Нетология
    Python-разработчик: расширенный курс + нейросети
    12 месяцев
    Далее
  • ProductStar × РБК
    Профессия: Python-разработчик + ИИ
    8 месяцев
    Далее
Пригласить эксперта
Ответы на вопрос 2
opium
@opium
Просто люблю качественно работать
тут не баг андроида, а перепутаны два механизма p4a. AndroidService(...).start() — старый способ под service/main.py, он вообще не трогает сервис из spec (services = chatmail_service:service/background_check.py). Именованный сервис p4a оборачивает в свой java-класс, стартовать его надо так:
from jnius import autoclass
activity = autoclass('org.kivy.android.PythonActivity').mActivity
autoclass(f'{activity.getPackageName()}.ServiceChatmail_service').start(activity, '')
. В spec допиши :foreground:foregroundServiceType=dataSync к services, разреши FOREGROUND_SERVICE, FOREGROUND_SERVICE_DATA_SYNC, POST_NOTIFICATIONS. На Android 15 у dataSync-сервиса лимит ~6ч в сутки — для вечного polling лучше WorkManager или push.
Ответ написан
Комментировать
Lord_of_Rings
@Lord_of_Rings Куратор тега Python
Northern Strider
Просто поделюсь своей реализацией fireground сервиса в одном из своих проектов:

Управление запуском/остановкой
https://github.com/GVCoder09/NoDPI4Android/blob/ma...
"""
Android service
"""

import logging

from android import mActivity  # pylint: disable=no-name-in-module
from jnius import autoclass, cast  # pylint: disable=no-name-in-module


def is_service_running(name_service: str) -> bool:
    """Check if service is running"""

    PythonActivity = autoclass("org.kivy.android.PythonActivity")
    context = PythonActivity.mActivity

    activity_manager = cast(
        "android.app.ActivityManager",
        context.getSystemService(context.ACTIVITY_SERVICE),
    )

    running_services = activity_manager.getRunningServices(1000)

    for service in running_services:
        logging.info(service.service.getClassName())
        if name_service in service.service.getClassName():
            return True
    return False


def start_service(name_service: str) -> None:
    """Start service"""

    context = mActivity.getApplicationContext()
    service = autoclass(str(context.getPackageName()) +
                        ".Service" + name_service)
    service.start(mActivity, "")


def stop_service(name_service: str) -> None:
    """Stop service"""

    context = mActivity.getApplicationContext()
    service = autoclass(str(context.getPackageName()) +
                        ".Service" + name_service)
    service.stop(mActivity)


Объявление сервиса:
[android]

services = proxy:%(source.dir)s/service.py:foreground:sticky
android.permissions = INTERNET,FOREGROUND_SERVICE,POST_NOTIFICATIONS,REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы