You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kivy闹钟应用打包APK后无法后台运行的问题排查与配置咨询

解决Kivy闹钟应用后台持续运行问题

核心问题诊断

你的代码直接在主App中启动AndroidService,但Kivy的Clock调度依赖UI线程,应用退到后台后,Android系统会优先挂起非前台的UI进程,导致定时任务失效。同时,当前的Service未以前台服务运行(Android 8.0+对后台服务有严格限制),也没把闹钟逻辑放到独立的Service进程中,这是后台保活失败的关键。

代码修正方案

1. 拆分独立的Service脚本

创建service.py文件,把闹钟定时、响铃逻辑移到Service内部,使用Android原生AlarmManager实现精准定时(比Kivy Clock更可靠),同时将Service设为前台服务:

from android import AndroidService, mActivity
from android.permissions import request_permissions, Permission
from jnius import autoclass, cast
from kivy.core.audio import SoundLoader
import time

# 请求必要权限
request_permissions([Permission.FOREGROUND_SERVICE, Permission.WAKE_LOCK])

AlarmManager = autoclass('android.app.AlarmManager')
PendingIntent = autoclass('android.app.PendingIntent')
Intent = autoclass('android.content.Intent')
Context = autoclass('android.content.Context')
NotificationManager = autoclass('android.app.NotificationManager')
NotificationChannel = autoclass('android.app.NotificationChannel')
Notification = autoclass('android.app.Notification')
System = autoclass('java.lang.System')

class AlarmService(AndroidService):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.alarm_sound = SoundLoader.load('alarm.mp3')
        self.channel_id = "alchemy_alarm_channel"
        self.create_notification_channel()

    def create_notification_channel(self):
        # 创建通知渠道(Android 8.0+必需)
        channel = NotificationChannel(
            self.channel_id,
            "Alarm Notifications",
            NotificationManager.IMPORTANCE_HIGH
        )
        channel.setDescription("Alchemy Alarm Service Notifications")
        notification_manager = mActivity.getSystemService(Context.NOTIFICATION_SERVICE)
        notification_manager.createNotificationChannel(channel)

    def on_start(self, intent, start_id):
        # 构建前台通知,让Service保持运行
        notification = Notification.Builder(mActivity, self.channel_id) \
            .setContentTitle("Alchemy Alarm") \
            .setContentText("Alarm service running in background") \
            .setSmallIcon(autoclass('android.R')..drawable.ic_dialog_alert) \
            .build()
        
        # 启动为前台服务
        self.startForeground(1, notification)

        # 设置闹钟定时:6秒后第一次响铃,之后每15秒一次,共7次
        alarm_manager = cast(AlarmManager, mActivity.getSystemService(Context.ALARM_SERVICE))
        intent = Intent(mActivity, self.__class__)
        intent.setAction("RING_ALARM")
        
        for i in range(7):
            trigger_time = System.currentTimeMillis() + (6 + i * 15) * 1000
            pending_intent = PendingIntent.getService(mActivity, i, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)
            alarm_manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, trigger_time, pending_intent)

        return super().on_start(intent, start_id)

    def on_handle_intent(self, intent):
        # 处理闹钟触发事件
        if intent.getAction() == "RING_ALARM":
            self.ring_alarm()

    def ring_alarm(self):
        if self.alarm_sound:
            self.alarm_sound.play()
        # 可在此添加UI更新逻辑(若需同步显示)

    def on_destroy(self):
        # 停止前台服务
        self.stopForeground(True)
        super().on_destroy()

if __name__ == '__main__':
    service = AlarmService('Alchemy Alarm Service', 'Running')
    service.start()

2. 修改主App代码

主App仅负责启动Service,不再处理定时逻辑:

import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from android import AndroidService

class AlchemyApp(App):
    def build(self):
        self.layout = BoxLayout(orientation='vertical')

        self.label = Label(text='Alchemy', font_size=50, color=(0.8, 0.4, 0.1, 1))
        self.layout.add_widget(self.label)

        self.start_button = Button(text='Start', font_size=30, background_color=(0.8, 0.8, 0.1, 1))
        self.start_button.bind(on_press=self.start_alarm_service)
        self.layout.add_widget(self.start_button)

        return self.layout

    def start_alarm_service(self, instance):
        self.start_button.disabled = True
        # 启动后台Service
        service = AndroidService('Alchemy Alarm Service', 'running')
        service.start('service started')
        # 可根据需求设置按钮重新启用逻辑,或让Service结束后通知App

if __name__ == '__main__':
    AlchemyApp().run()

Buildozer.spec 配置调整

修改你的buildozer.spec文件,添加以下关键配置:

# 权限配置
android.permissions = FOREGROUND_SERVICE, WAKE_LOCK, RECEIVE_BOOT_COMPLETED
# 指定Service入口脚本
android.service = service.py
# 添加依赖
requirements = python3,kivy,pyjnius,android
# 目标SDK版本(建议适配最新版本)
android.api = 33
# 最小SDK版本(适配Android 8.0+)
android.minapi = 26
# 允许后台运行模式
android.background_mode = service
# 启用AndroidX(适配新特性)
android.use_androidx = True

关键注意事项

  • 前台服务必要性:Android 8.0+不允许后台服务长时间运行,必须通过前台通知让系统识别服务在运行,避免被回收。
  • AlarmManager使用:setExactAndAllowWhileIdle可保证设备休眠时也能触发闹钟,是闹钟类应用的可靠选择。
  • 权限申请:必须申请FOREGROUND_SERVICE和WAKE_LOCK权限,否则服务无法启动或唤醒设备。

内容的提问来源于stack exchange,提问作者alex1888

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 23:45:34