求助:如何使用Flet、Python及相关工具创建前台服务并完成配置?
Hey there! I’ve messed around with setting up foreground services using Flet, Pyjnius, and Python before, so let me share a step-by-step approach that worked for me, inspired by that Flutter stopwatch example you mentioned.
1. 先搞定依赖配置
First, make sure you’ve got the necessary packages installed. You’ll need flet for the UI, pyjnius to bridge Python and Android native APIs. Add these to your pyproject.toml:
[project] name = "flet-foreground-service" version = "0.1.0" dependencies = [ "flet>=0.22.0", "pyjnius>=1.4.2" ] [tool.flet] android_package = "com.yourdomain.fletforeground" android_launcher_name = "Flet Foreground Demo" [tool.flet.android] permissions = [ "android.permission.FOREGROUND_SERVICE", "android.permission.POST_NOTIFICATIONS" ] # 如果需要自定义AndroidManifest(比如注册服务或指定前台服务类型),添加下面一行 # android_manifest_path = "android/AndroidManifest.xml"
Then install them with pip install -e . or just pip install flet pyjnius.
2. 用Pyjnius编写前台服务逻辑
Foreground services are Android-native, so we’ll use Pyjnius to call Android’s API directly. Here’s a simplified example that mimics the stopwatch logic:
import flet as ft from jnius import autoclass, cast import threading import time # 加载Android原生类 Context = autoclass("android.content.Context") Intent = autoclass("android.content.Intent") PendingIntent = autoclass("android.app.PendingIntent") NotificationManager = autoclass("android.app.NotificationManager") NotificationChannel = autoclass("android.app.NotificationChannel") NotificationCompat = autoclass("androidx.core.app.NotificationCompat") Service = autoclass("android.app.Service") PythonService = autoclass("org.kivy.android.PythonService") # 全局变量存服务状态和计时器数据 is_running = False elapsed_time = 0 notification_manager = None def create_notification_channel(): # Android 8+ 必须创建通知渠道才能显示通知 global notification_manager notification_manager = cast(NotificationManager, ft.page.context.getSystemService(Context.NOTIFICATION_SERVICE)) channel = NotificationChannel( "stopwatch_channel", "Stopwatch Service", NotificationManager.IMPORTANCE_DEFAULT ) channel.setDescription("Foreground service for stopwatch tracking") notification_manager.createNotificationChannel(channel) def build_notification(): # 构建前台服务所需的通知 mins = elapsed_time // 60 secs = elapsed_time % 60 content_text = f"Elapsed: {mins:02d}:{secs:02d}" # 创建点击通知返回应用的意图 intent = Intent(ft.page.context, PythonService.class_) pending_intent = PendingIntent.getService(ft.page.context, 0, intent, PendingIntent.FLAG_IMMUTABLE) return NotificationCompat.Builder(ft.page.context, "stopwatch_channel")\ .setContentTitle("Stopwatch Running")\ .setContentText(content_text)\ .setSmallIcon(ft.page.context.getResources().getIdentifier("ic_launcher", "mipmap", ft.page.context.getPackageName()))\ .setContentIntent(pending_intent)\ .build() class ForegroundService(Service): def onStartCommand(self, intent, flags, startId): global is_running, elapsed_time create_notification_channel() # 启动前台服务,传入通知ID和通知实例 self.startForeground(1, build_notification()) # 后台运行计时器逻辑 def update_timer(): global elapsed_time while is_running: elapsed_time += 1 # 更新通知内容 notification_manager.notify(1, build_notification()) time.sleep(1) threading.Thread(target=update_timer, daemon=True).start() return Service.START_STICKY def toggle_service(e): global is_running if not is_running: is_running = True # 启动前台服务 intent = Intent(ft.page.context, ForegroundService.class_) ft.page.context.startForegroundService(intent) e.control.text = "Stop Service" else: is_running = False # 停止前台服务 intent = Intent(ft.page.context, ForegroundService.class_) ft.page.context.stopService(intent) e.control.text = "Start Service" ft.page.update() def main(page: ft.Page): page.title = "Flet Foreground Service Demo" page.add(ft.ElevatedButton("Start Service", on_click=toggle_service, width=200)) if __name__ == "__main__": ft.app(target=main)
3. 调整AndroidManifest(适配高版本Android)
If you’re targeting Android 12+, you need to specify the foreground service type (e.g., type="timer" for stopwatch). Create a custom AndroidManifest.xml in an android folder at your project root:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.yourdomain.fletforeground"> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <application android:label="Flet Foreground Demo" android:icon="@mipmap/ic_launcher"> <activity android:name="io.flutter.embedding.android.FlutterActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <!-- 注册Python服务 --> <service android:name="org.kivy.android.PythonService" android:foregroundServiceType="timer" android:exported="false"/> <!-- 注册我们的前台服务 --> <service android:name=".ForegroundService" android:foregroundServiceType="timer" android:exported="false"/> </application> </manifest>
Then update your pyproject.toml to point to this manifest:
[tool.flet.android] android_manifest_path = "android/AndroidManifest.xml"
4. 打包并测试
Run the command to build your Android app:
flet build android
Once the build finishes, install the APK on an Android device or emulator. Tap the "Start Service" button, and you should see a persistent notification with the running stopwatch—even if you minimize the app!
A few key notes to avoid headaches:
- Android Version Compatibility: For Android 12+, don’t forget the
foregroundServiceTypeattribute in the manifest. - Threading: Never run long-running tasks on the main UI thread—use daemon threads like in the example to keep the app responsive.
- Notification Updates: Always use
NotificationManager.notify()to refresh the notification content after changes.
Hope this helps you get your foreground service up and running! Let me know if you hit any snags along the way.
备注:内容来源于stack exchange,提问作者Peter Kačmarík




