When the main app is running but not active the service notification click brings it to front, that's Ok. Another case when I close the app by swiping it out from the recent apps list, the service is restarted and when I click the notification just black screen is shown and that's all.
The one interesting thing I noticed, after some time (an hour or two) it gets working - the main app is started on service notification click! How can I achieve that once the service restarted?
The app is created with Python+Kivy and built with Buildozer and I run it on Android 10.
I tried setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
for the Intent but it didn't help.
Here is some code. PythonService.java:
package org.kivy.android;
import android.os.Build;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import android.app.Service;
import android.os.IBinder;
import android.os.Bundle;
import android.content.Intent;
import android.content.Context;
import android.util.Log;
import android.app.Notification;
import android.app.PendingIntent;
import android.os.Process;
import java.io.File;
//imports for channel definition
import android.app.NotificationManager;
import android.app.NotificationChannel;
import android.graphics.Color;
public class PythonService extends Service implements Runnable {
// Thread for Python code
private Thread pythonThread = null;
// Python environment variables
private String androidPrivate;
private String androidArgument;
private String pythonName;
private String pythonHome;
private String pythonPath;
private String serviceEntrypoint;
// Argument to pass to Python code,
private String pythonServiceArgument;
public static PythonService mService = null;
private Intent startIntent = null;
private boolean autoRestartService = false;
public void setAutoRestartService(boolean restart) {
autoRestartService = restart;
}
public int startType() {
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (pythonThread != null) {
Log.v("python service", "service exists, do not start again");
return START_NOT_STICKY;
}
startIntent = intent;
Bundle extras = intent.getExtras();
androidPrivate = extras.getString("androidPrivate");
androidArgument = extras.getString("androidArgument");
serviceEntrypoint = extras.getString("serviceEntrypoint");
pythonName = extras.getString("pythonName");
pythonHome = extras.getString("pythonHome");
pythonPath = extras.getString("pythonPath");
boolean serviceStartAsForeground = (
extras.getString("serviceStartAsForeground").equals("true")
);
pythonServiceArgument = extras.getString("pythonServiceArgument");
pythonThread = new Thread(this);
pythonThread.start();
if (serviceStartAsForeground) {
doStartForeground(extras);
}
return startType();
}
protected int getServiceId() {
return 1;
}
protected void doStartForeground(Bundle extras) {
String serviceTitle = extras.getString("serviceTitle");
String serviceDescription = extras.getString("serviceDescription");
Notification notification;
Context context = getApplicationContext();
Intent contextIntent = new Intent(context, PythonActivity.class);
contextIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // my attempt but it didn't helped
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
notification = new Notification(
context.getApplicationInfo().icon, serviceTitle, System.currentTimeMillis());
try {
// prevent using NotificationCompat, this saves 100kb on apk
Method func = notification.getClass().getMethod(
"setLatestEventInfo", Context.class, CharSequence.class,
CharSequence.class, PendingIntent.class);
func.invoke(notification, context, serviceTitle, serviceDescription, pIntent);
} catch (NoSuchMethodException | IllegalAccessException |
IllegalArgumentException | InvocationTargetException e) {
}
} else {
// for android 8+ we need to create our own channel
// https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a"; //TODO: make this configurable
String channelName = "Background Service"; //TODO: make this configurable
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(chan);
Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID);
builder.setContentTitle(serviceTitle);
builder.setContentText(serviceDescription);
builder.setContentIntent(pIntent);
builder.setSmallIcon(context.getApplicationInfo().icon);
notification = builder.build();
}
startForeground(getServiceId(), notification);
}
@Override
public void onDestroy() {
super.onDestroy();
pythonThread = null;
if (autoRestartService && startIntent != null) {
Log.v("python service", "service restart requested");
startService(startIntent);
}
Process.killProcess(Process.myPid());
}
/**
* Stops the task gracefully when killed.
* Calling stopSelf() will trigger a onDestroy() call from the system.
*/
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
stopSelf();
}
@Override
public void run(){
String app_root = getFilesDir().getAbsolutePath() + "/app";
File app_root_file = new File(app_root);
PythonUtil.loadLibraries(app_root_file,
new File(getApplicationInfo().nativeLibraryDir));
this.mService = this;
nativeStart(
androidPrivate, androidArgument,
serviceEntrypoint, pythonName,
pythonHome, pythonPath,
pythonServiceArgument);
stopSelf();
}
// Native part
public static native void nativeStart(
String androidPrivate, String androidArgument,
String serviceEntrypoint, String pythonName,
String pythonHome, String pythonPath,
String pythonServiceArgument);
}
and the class extending PythonService:
package org.test.schedules;
import android.content.Intent;
import android.content.Context;
import org.kivy.android.PythonService;
public class ServiceService extends PythonService {
@Override
protected int getServiceId() {
return 1;
}
static public void start(Context ctx, String pythonServiceArgument) {
Intent intent = new Intent(ctx, ServiceService.class);
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
intent.putExtra("androidPrivate", ctx.getFilesDir().getAbsolutePath());
intent.putExtra("androidArgument", argument);
intent.putExtra("serviceTitle", "Schedules1106fgs_sdk28");
intent.putExtra("serviceDescription", "Service");
intent.putExtra("serviceEntrypoint", "service/main.py");
intent.putExtra("pythonName", "service");
intent.putExtra("serviceStartAsForeground", "true");
intent.putExtra("pythonHome", argument);
intent.putExtra("pythonPath", argument + ":" + argument + "/lib");
intent.putExtra("pythonServiceArgument", pythonServiceArgument);
ctx.startService(intent);
}
static public void stop(Context ctx) {
Intent intent = new Intent(ctx, ServiceService.class);
ctx.stopService(intent);
}
}
Also here is a piece of AndroidManifest.xml
<activity android:name="org.kivy.android.PythonActivity"
android:label="@string/app_name"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale|uiMode|uiMode|screenSize|smallestScreenSize|layoutDirection"
android:screenOrientation="portrait"
android:launchMode="singleTask"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="org.kivy.android.PythonService"
android:process=":pythonservice" />
<service android:name="org.test.schedules.ServiceService"
android:process=":service_service" />
Will be much appreciated for your help
Update
I haven't found the solution yet but noticed but seems the issue is related to background cached process. So if I go to Developer options -> Running services -> Show cached processes and stop the process there manually the app restarts fine. I guess the solution could be somehow to prevent creating the cached process in the background or to kill that process at some specific moment, on app exit or service restart, I'm not an expert in Android, can anyone help with this?