-
Notifications
You must be signed in to change notification settings - Fork 0
Creating a foreground service
Fabian edited this page Aug 27, 2026
·
1 revision
Things you'll need
- For Android 14+, To choose a foreground service type from android docs
According to p4a docs we need to specify it's going to be a foreground service in buildozer.spec
services = Mydownloader:./services/mydownloader.py:foreground
# my package for notifications
requirements = python3, kivy, pyjnius, android-notify
# Add permission for notifications
android.permissions = POST_NOTIFICATIONSTo automate this process from your buildozer.spec by passing in an argument for p4a.hook
p4a.hook = p4a/hook.pyYou have to use foreground service type fitting your use-case.
For this example I'm using dataSync which is for download|uploads.
Programmatically found my service and added a type argument:
# p4a/hook.py
from pathlib import Path
from pythonforandroid.toolchain import ToolchainCL
def after_apk_build(toolchain: ToolchainCL):
manifest_file = Path(toolchain._dist.dist_dir) / "src" / "main" / "AndroidManifest.xml"
text = manifest_file.read_text(encoding="utf-8")
# Change these three lines to fit your use-case
package = "org.laner.lan_ft" # Find value in your buildozer.spec "package.domain.package.name"
service_name="Mydownloader"
foreground_type="dataSync"
target = f'android:name="{package}.Service{service_name.capitalize()}"'
# Inject foregroundServiceType
pos = text.find(target)
if pos != -1:
end = text.find("/>", pos)
text = (
text[:end] +
' android:foregroundServiceType="{foreground_type}"' +
text[end:]
)
print("Successfully Added foregroundServiceType to ServiceMydownloader")
text = text.replace("</application>", f"{receiver_xml}\n</application>")
print("Successfully added Receiver")
# Write back the final manifest
manifest_file.write_text(text, encoding="utf-8")
print("Manifest update completed",text)- You must add
FOREGROUND_SERVICEandPOST_NOTIFICATIONS - According to your foreground type you might have an additional permission in my case only
FOREGROUND_SERVICE_DATA_SYNC
android.permissions = FOREGROUND_SERVICE, POST_NOTIFICATIONS, FOREGROUND_SERVICE_DATA_SYNC# main.py
def start_service():
from android import mActivity
from jnius import autoclass
context = mActivity.getApplicationContext()
service_name="Mydownloader"
service_class=autoclass(context.getPackageName() + '.Service' + service_name.capitalize())
service.start(self.mActivity, "port number")Reduced boilerplate with android-notify
# ./services/mydownloader.py
print("Entered Service File...")
import time,os
from jnius import autoclass
from android_notify import Notification
# get and see parameter from ui
Notification(title = os.environ.get('PYTHON_SERVICE_ARGUMENT', 'no arg')).send()
BuildVersion = autoclass("android.os.Build$VERSION")
ServiceInfo = autoclass("android.content.pm.ServiceInfo")
PythonService = autoclass('org.kivy.android.PythonService')
service = PythonService.mService
foreground_type= ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC if BuildVersion.SDK_INT >= 30 else 0
fmt = lambda s: f"{int(s//3600)}h {int((s%3600)//60)}m {int(s%60)}s"
n=Notification(title="Foreground Service Active", message="This service is running in the foreground")
builder=n.start_building() # not using .send() allowing .startForeground() to send initial notification
service.startForeground(n.id, builder.build(), foreground_type)
service.setAutoRestartService(True) # auto-restart if killed
print("Foreground Service is alive. Entering main loop...")
n1 = Notification(title="Running for 0h 0m 0s")
n1.send()
start = time.time()
while True:
elapsed = time.time() - start
n1.updateTitle(f"Running for {fmt(elapsed)}")
time.sleep(2)