Starting Kivy App and Service on bootup on Android

thekrine edited this page Aug 30, 2018 · 8 revisions

Goal: to start a Kivy service on bootup.

It's assumed that you already know how to create a service using Python-for-Android arbitrary service scripts. For example, let it be something simple:

from time import sleep

if __name__ == '__main__':
    while True:
        print "myapp service"
        sleep(5)

We need to listen to the boot complete event in Android, so add "RECEIVE_BOOT_COMPLETED" in your buildozer.spec file under android.permissions

A BroadcastReceiver is required to listen to this event. As there is no way that I know of for the build to input such a receiver in the AndroidManifest.xml, you need to alter AndroidManifest.tmpl.xml (<PROJECT FOLDER>/.buildozer/android/platform/build/dists/<PROJECT NAME>/templates/AndroidManifest.tmpl.xml) with the following as a direct child of <application>:

<receiver android:name=".MyBroadcastReceiver" android:enabled="true" >
    <intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter>
</receiver>

Then build will output it into AndroidManifest.xml anyways. Note the "." in front of "MyBroadcastReceiver" tells Java that it belongs to the package in question.

Because on boot up there is no Python interpreter, the BroadcastReceiver cannot be coded in Python and adb logcat will report Android unable to load the Java class version of it. So it must be written in Java.

Here is MyBroadcastReceiver.java that starts Kivy App (not Service!):

package org.myproject.myapp;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;
import org.kivy.android.PythonActivity;

public class MyBroadcastReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        Intent ix = new Intent(context, PythonActivity.class);
        ix.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(ix);
    }
}

Here org.myproject.myapp is the package in question specified in your build parameters. Note that FLAG_ACTIVITY_NEW_TASK must be used as we are starting from boot and our app hasn't started yet. Place the Java file in ('PROJECT FOLDER>/.buildozer/android/platform/build/dists/PROJECT NAME/src/main/java/PACKAGE DOMAIN/PACKAGE DOMAIN/PROJECT NAME/') folder. The last three folders of your file path should be your package domain + name, eg: org/myproject/myapp/. If all is correct your ServiceMyservice file should also fall under the myapp folder, just for some reference.

Well, this code start the app but not the service. But usually we need to start the background service and not the app. This is also quite simple if you know what to do. Exactly we need to start the service instead of activity and put some extras:

package org.myproject.myapp;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;
import org.myproject.myapp.ServiceMyservice;

public class MyBroadcastReceiver extends BroadcastReceiver {
    public void onReceive(Context context, Intent intent) {
        String package_root = context.getFilesDir().getAbsolutePath();
        String app_root =  package_root + "/app";
        Intent ix = new Intent(context, ServiceMyservice.class);
        ix.putExtra("androidPrivate", package_root);
        ix.putExtra("androidArgument", app_root);
        ix.putExtra("serviceEntrypoint", "./service/main.py");
        ix.putExtra("pythonName", "myservice");
        ix.putExtra("pythonHome", app_root);
        ix.putExtra("pythonPath", package_root);
        ix.putExtra("pythonServiceArgument", app_root+":"+app_root+"/lib");
        ix.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startService(ix);
    }
}

That's all.

Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.