Skip to content

Android Background Services

xloem edited this page Dec 14, 2020 · 1 revision
A at 9:05 AM
How do I keep my service running after closing the application? And can you provide a small example
SERVICE_NAME = u '{packagename} .Service {servicename}'. Format (
        packagename = u'org.test.test ',
        servicename = u'Notifier ') 
service = autoclass (SERVICE_NAME)
mActivity = autoclass (u'org.kivy.android.PythonActivity '). mActivity
argument = ''
service.start (mActivity, argument)
B at 9:13 AM
(i found https://blog.kivy.org/2014/01/building-a-background-application-on-android-with-kivy/ using google, no experience with services myself)
Kivy Blog
gabriel
Building a background application on android with Kivy.
C at 10:21 AM
You can't
Only service you can run until your app is not killed would be foreground service.
You must notify user it's foreground. When starting kivy's service use argument foreground=True
Then if you want periodically wake your app you need a BroadcastReceiver
A at 10:46 AM
Please, give me small example
@B As I said, the application is very simple, it checks the battery level even if the application is closed
B at 10:48 AM
Your app is going to drain more battery then anything else:sweat_smile: :sweat_smile:
Just create a BroadcastReceiver Java  class and then onReceive method start your service.
There's example somebody posted in your project thread
A at 11:04 AM
@B Let's go in order, otherwise I have never worked with Java) So we run Broadcastreceiver self.br = BroadcastReceiver (
                 self.on_broadcast, actions = ['battery_changed'])
             self.br.start ()
B at 11:05 AM
No you don't need that one
A at 11:05 AM
...
C at 11:05 AM
You need Java one
A at 11:05 AM
My service -     def service(self):
        try:
            service = autoclass(SERVICE_NAME)
            mActivity = autoclass(u'org.kivy.android.PythonActivity').mActivity
            argument = ''
            service.start(mActivity, argument)
            self.service = service
        except Exception as err:
            print('serviceErr', err)
A at 11:05 AM
Import BroadcastReceiver from Java?
BroadcastReceiver_java = autoclass("android.content.BroadcastReceiver") like that?
C at 11:07 AM
There's a class called GenericBroadcastReceiver.java inside your project folder find it and see how it's done.
Then when you see it we can pass to subsequent question.
A at 11:08 AM
1 minute
I opened this file
package org.kivy.android;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;

public class GenericBroadcastReceiver extends BroadcastReceiver {

    GenericBroadcastReceiverCallback listener;

    public GenericBroadcastReceiver(GenericBroadcastReceiverCallback listener) {
        super();
        this.listener = listener;
    }

    public void onReceive(Context context, Intent intent) {
        this.listener.onReceive(context, intent);
    }
}
@C But this is Java
C at 11:12 AM
Exactly
Now you need similar one
Without listener
That listener is Java interface which you interact with in python because somebody already translated everything for you, now you have to do it yourself.
Basically you have to copy everything (name your class differently) and delete everything related to listener.
A at 11:16 AM
from jnius import autoclass

context = autoclass("android.content.Context")
Intent = autoclass('android.content.Intent')
BroadcastReceiver = autoclass("android.content.BroadcastReceiver")

class GenericBroadcastReceiver(BroadcastReceiver):
    def onReceive(self, context, Intent):
        ....onReceive(context, Intent)
Some thing like that?
Then what's before onRecive
C at 11:17 AM
No no it has to be almost identical to that one above.
A at 11:17 AM
With a listener????
C at 11:17 AM
class MyBroadcastReceiver(BroadcastReceiver):
    def onReceive(self, context, Intent):
Like this
It has to be real Java class
A at 11:18 AM
What's in the function onRecieve?
C at 11:18 AM
There's no self in java
You start your service there
You have to import PythonActivity
A at 11:20 AM
And as without self, we are in python?
C at 11:20 AM
Create static activity and then call start service from PythonActivity
No we are not in python
This is Java part of your app
You can create a brodcas receiver in python
A at 11:22 AM
Oy, I need to create .java nearby mian.py
?
C at 11:22 AM
You can later start it from python
No no
Go see how that GenericBroadcatReceiver is created
A at 11:23 AM
I see
it is java class
C at 11:23 AM
Aha
A at 11:23 AM
in .java file
C at 11:23 AM
Aha
A at 11:24 AM
And I dont need .java
C at 11:24 AM
Yes you did
A at 11:24 AM
from jnius import autoclass

context = autoclass("android.content.Context")
Intent = autoclass('android.content.Intent')
BroadcastReceiver = autoclass("android.content.BroadcastReceiver")

class GenericBroadcastReceiver(BroadcastReceiver):
    @staticmethod
    def onReceive(context, Intent):
        service = autoclass(SERVICE_NAME)
        mActivity = autoclass(u'org.kivy.android.PythonActivity').mActivity
        argument = ''
        service.start(mActivity, argument)
?
...
C at 11:24 AM
No
A at 11:24 AM
...
C at 11:24 AM
It's not like that
It's little more complex
This is Java part of your app
A at 11:26 AM
But where should I put my Java code?
I was thinking of converting to python
But you say I did not
C at 11:26 AM
You need to create a Java class inside a .java file and leave it close to thatone
A at 11:27 AM
So I need a .java file
C at 11:27 AM
Aha
That is the only way if you want to trigger alarm manager
A at 11:28 AM
Like MyJava.java next to main.py
C at 11:28 AM
No
A at 11:29 AM
I don't understand something then where to place it(
And what to post
C at 11:30 AM
Let's say class MyBatteryPoll extends BroadcastReceiver in  MyBatteryPoll.java
A at 11:30 AM
Where?
C at 11:30 AM
And leave it close to GenericBroadcatReceiver
A at 11:30 AM
In GenericBroadcatReceiver.java?
C at 11:30 AM
Ahh
A at 11:31 AM
under public class GenericBroadcastReceiver extends BroadcastReceiver
create same code without listener
C at 11:31 AM
Not under
A at 11:31 AM
Replace?
C at 11:31 AM
Close to it, where all other Kivy classes are
No replace
Create your own
A at 11:33 AM
/home/USERNAME/MYAPP/.buildozer/android/platform/build-arm64-v8a/dists/MYAPP__arm64-v8a/src/main/java/org/kivy/android here
C at 11:33 AM
No
Sdl bootstrap
A at 11:35 AM
@C /home/USERNAME/MYAPP/.buildozer/android/platform/python-for-android/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android
Here
I'll create java file
What's next?
I have to rename the class?
C at 11:39 AM
As I said before
A at 11:39 AM
package org.kivy.android;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;

public class GenericBroadcastReceiver extends BroadcastReceiver {


    public void onReceive(Context context, Intent intent) {
        this.listener.onReceive(context, intent);
    }
}
?
C at 11:40 AM
Remove everything related to listener
A at 11:48 AM
@C package org.kivy.android;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;

public class GenericBroadcastReceiver extends BroadcastReceiver {


    public void onReceive(Context context, Intent intent) {
    }
}
Delete
I'm about to begin to understand something, but that's what will be inside onReceive
C at 12:06 PM
context.sendBroadcast(new Intent("BATTERY_STATE"));
and then inside your PythonActivity class add this
after onCreate add this line 
registerReceiver(broadcastReceiver, new IntentFilter("BATTERY_STATE"));
and then add this
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
       // start_service(check here for arguments what it takes)
    }
};
also add this line in onDestroy() 
unregisterReceiver(broadcastReceiver);
that's it your sevice it's going to start and do what is supposed to do
A at 12:15 PM
ohh
A at 12:15 PM
It is in PythonActivity class in .java?
You somehow described it very difficult
Hard to make out
@C What we write in MyBatteryPoll.java ?
MyBatteryPoll.java - package org.kivy.android;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;

public class GenericBroadcastReceiver extends BroadcastReceiver {

    public void onReceive(Context context, Intent intent) {
        context.sendBroadcast(new Intent("BATTERY_STATE"));
    }
}
?
C at 12:23 PM
Yes
A at 12:24 PM
Where?
C at 12:25 PM
Just a second
A at 12:25 PM
:thumbsup:
C at 12:33 PM
this is how it should look

A at 12:38 PM
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.v(TAG, "PythonActivity onCreate running");
        resourceManager = new ResourceManager(this);

        Log.v(TAG, "About to do super onCreate");
        super.onCreate(savedInstanceState);
        Log.v(TAG, "Did super onCreate");

        this.mActivity = this;
        this.showLoadingScreen();

        new UnpackFilesTask().execute(getAppRoot());
        registerReceiver(broadcastReceiver, new IntentFilter("BATTERY_STATE"));
    } like that?
A at 12:38 PM
Add this after onCreate?
C at 12:42 PM
yeah this line 
registerReceiver(broadcastReceiver, new IntentFilter("BATTERY_STATE"));
then add this method too
actually is not a method, however add it
A at 12:43 PM
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.v(TAG, "PythonActivity onCreate running");
        resourceManager = new ResourceManager(this);

        Log.v(TAG, "About to do super onCreate");
        super.onCreate(savedInstanceState);
        Log.v(TAG, "Did super onCreate");

        this.mActivity = this;
        this.showLoadingScreen();

        new UnpackFilesTask().execute(getAppRoot());
    }
    registerReceiver(broadcastReceiver, new IntentFilter("BATTERY_STATE"));
    BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
           // start_service(check here for arguments what it takes)
        }
    };
C at 12:43 PM
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // start_service(check for arguments down the code)
        }
    };
A at 12:43 PM
?
C at 12:44 PM
no inside a method
outside
between two methods
A at 12:45 PM
what two methods
C at 12:45 PM
what ever two methods
not inside
A at 12:46 PM
So kind of outside onCreate the same?
C at 12:46 PM
yes
like you would add some other method inside a class
A at 12:47 PM
@C
Attachment file type: document
PythonActivity.txt
23.66 KB
Like after adding...
C at 12:48 PM
no, this one inside onCreate() registerReceiver(broadcastReceiver, new IntentFilter("BATTERY_STATE"));
A at 12:49 PM
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.v(TAG, "PythonActivity onCreate running");
        resourceManager = new ResourceManager(this);

        Log.v(TAG, "About to do super onCreate");
        super.onCreate(savedInstanceState);
        Log.v(TAG, "Did super onCreate");

        this.mActivity = this;
        this.showLoadingScreen();

        new UnpackFilesTask().execute(getAppRoot());
        registerReceiver(broadcastReceiver, new IntentFilter("BATTERY_STATE"));
    }
    
    BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
           // start_service(check here for arguments what it takes)
        }
    };
C at 12:49 PM
yes
A at 12:49 PM
EEEEEEEEEEEE
OK, I have two files
C at 12:50 PM
what two files?
A at 12:50 PM
Attachment file type: code
MyBatteryPoll.java
312 bytes
Attachment file type: code
PythonActivity.java
23.07 KB
C at 12:50 PM
python activity was always there
you just added one
A at 12:51 PM
How do I perform the action now?
C at 12:51 PM
now you need this
<receiver
        android:name="org.kivy.android.MyBatteryPoll"
        android:enabled="true"
        android:exported="false">
        <intent-filter>
            <action android:name="org.kivy.android.BATTERY_ALARM" />
        </intent-filter>
    </receiver>
this part goes to manifest
I edited
it
check it again
A at 12:52 PM
This?
in android/platform/build.../dists/package/templates/AndroidManifest.tmpl.xml?
C at 12:53 PM
I edited it again
check it again
A at 12:54 PM
What IT?
A at 12:54 PM
?
C at 12:54 PM
manifest part
A at 12:54 PM
Here my manifest - android/platform/build.../dists/package/templates/AndroidManifest.tmpl.xml?
C at 12:55 PM
yes it should be template
A at 12:55 PM
?

@C  where exactly to add receiver?
Attachment file type: document
Manifest.txt
5.78 KB
Somewhere around here? {% if args.billing_pubkey %}
<service android:name="org.kivy.android.billing.BillingReceiver"
android:process=":pythonbilling" />
<receiver android:name="org.kivy.android.billing.BillingReceiver"
android:process=":pythonbillingreceiver">
<intent-filter>
<action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
<action android:name="com.android.vending.billing.RESPONSE_CODE" />
<action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
</intent-filter>
</receiver>
{% endif %}
C at 1:03 PM
and this is python part
from jnius import autoclass, cast
MyBatteryPoll = autoclass('org.kivy.android.MyBatteryPoll')
PythonActivity = autoclass('org.kivy.android.PythonActivity')
context = PythonActivity.mActivity
Intent = autoclass('android.content.Intent')
AlarmManager = autoclass('android.app.AlarmManager')
PendingIntent = autoclass('android.app.PendingIntent')
Context = autoclass('android.content.Context')
SystemClock = autoclass('android.os.SystemClock')


def start_alarm(self):

    alarmIntent = Intent()
    alarmIntent.setClass(context, MyBatteryPoll)
    alarmIntent.setAction("org.kivy.android.BATTERY_ALARM")
    pendingIntent = PendingIntent.getBroadcast(
        context, 1234, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    alarm = cast(
        AlarmManager, context.getSystemService(Context.ALARM_SERVICE))
    alarm.setInexactRepeating(
        AlarmManager.ELAPSED_REALTIME_WAKEUP,
        SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR,
        AlarmManager.INTERVAL_HALF_HOUR, pendingIntent)
A at 1:04 PM
@C And where to push the service in the manifest
And how do I call my function in python code then
C at 1:06 PM
its in your broadcast receiver inside PythonActivity
I commented out that part it says check for arguments
you call your service there
A at 1:07 PM
So, let's take it in order
A at 1:07 PM
.
C at 1:07 PM
everything else is done as you would do when setting up service from python side
It should look something like this

now you do rest
and debug it
A at 1:10 PM
@C Here you have thrown off the python code
Where it should be
A at 1:12 PM
this code
C at 1:15 PM
in your app you call it once, it's going to wake up and start a service every half hour, also stop your service after you did what you have to do.
that's python
A at 1:15 PM
Add this code in my main class?
C at 1:16 PM
wherever you want, it's up to you
when you call it it's going to start scheduling alarm every 30 minutes
it's not exact thought
A at 1:17 PM
And where can I specify the function that will be performed on the call?
Here is how Service will work, and what function it will perform, where can I specify it
C at 1:20 PM
what do you mean?
A at 1:21 PM
Well, this was all done for what, I have a function that pronounces some text, like a low charge level, how to make it work, something like bind
C at 1:22 PM
this will start your service
you do whatever you want from your service
you asked for something in the background
every 30 minutes this will start your service
A at 1:26 PM
code
Attachment file type: document
message.txt
2.77 KB
C at 1:26 PM
What?
A at 1:26 PM
Well let's say that main.py
    def on_start(self):
        self.service()
        self.start_alarm() there I run service and alarm
I think I'm doing something wrong
C at 1:27 PM
Everything hehe:sweat_smile:
A at 1:27 PM
))))))))))))))
i just can't understand what it is at all, i just need to run the say function in the background
C at 1:28 PM
You call that method I gave you from python
A at 1:29 PM
    def on_start(self):
        self.start_alarm()?
C at 1:30 PM
That method will trigger a broadcast receiver which is going to trigger another one in your app and then from there your service is going to start
And it's scheduled for 30 minutes
A at 1:31 PM
Do I just need to run this function?
C at 1:31 PM
Yes
Don't do it on start
A at 1:31 PM
And my servese/,ain.py start working
A at 1:31 PM
on_stop?
C at 1:32 PM
You still have to call startservice from PythonActivity class
The one I commented
Just go down the code and you'll see how python service is started in Kivy, we did a lot of low level stuff here. Better start reading.
:sweat_smile: :sweat_smile:
A at 1:34 PM
Well, if I run this code now, will it work?
I get error
When I build app
(
Attachment file type: document
log.txt
37.75 KB
C at 1:38 PM
AndroidManifest.xml; lineNumber: 95; columnNumber: 10; The value of attribute "android:name" associated with an element type "action" must not contain the '<' character.
A at 1:39 PM
I see
C at 1:39 PM
This is the last one, I'm not debugging your app
A at 1:39 PM
Attachment file type: document
Manifest.txt
6.11 KB
@C There's definitely no other way to run the app in the background
C at 1:46 PM
Nope
On android there's no.
A at 1:47 PM
Or maybe you can just fix it and not let it close?
C at 2:21 PM
It's in manifest you added
Check the screenshot I sent you and make it similar
There's < more than it should be
A at 2:29 PM
Thank you, maybe something will work out
B at 2:38 PM
Neizvestnyi is your project public anywhere?  thinking on background services.  Sounds like there's a reason that battery would be an issue ...
A at 2:43 PM
@B I do it for myself, it was just interesting how to make sure that when you close some processes are not killed
B at 3:06 PM
thanks maybe there's a kivy wiki or something somewhere I could put your conversation into

Clone this wiki locally