javaandroidservice

How to make my Android service remains running under normal circumstances?


I want to make an Android service that can run independent of the main app. It will be a TCP server to receive messages. The project is created and managed with BeeWare, not with Android Studio. I configured the manifest to run the service in a separeted process and when the service is started, I elevated it to the foreground service. Now, if I go to Task Manager and swipe away my app, the service indeed remains running. But if I press the X button to clear all recent apps, the service is killed too. What do I need to do so that my service remains running under all nomal circumstances ? I have enough RAM and the battery is full, so it shouldn't kill my service. I have Android 13 on Xiaomi Redmi Note 10 Pro.

Note: My question is not a dupplicate of these questions because they are 10 years old, and the things got changed and they work differently:

For reference, this is my manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" />
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/formal_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme.Launcher" >
        <!-- https://developer.android.com/guide/topics/resources/runtime-changes#HandlingTheChange -->
        <activity
            android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
            android:name="org.beeware.android.MainActivity"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <service 
            android:name="org.beeware.android.MyService" 
            android:foregroundServiceType="remoteMessaging"
            android:exported="false"
            android:process=":tcp_service_process"
            android:icon="@mipmap/ic_launcher_round">
        </service>    
        
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.example.myapp.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths">
            </meta-data>
        </provider>
        
    </application>
    
</manifest>

And this is my service:

package org.beeware.android;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.os.Build;
import androidx.core.app.NotificationCompat;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
import android.util.Log;

import com.chaquo.python.PyObject;
import com.chaquo.python.Python;
import com.chaquo.python.android.AndroidPlatform;

import com.example.myapp.R;


public class MyService extends Service {

  private static final String ChannelID = "MyServiceChannel";
  private String mySrv = "MyService";
  private int ONGOING_NOTIFICATION_ID = 1; // This cannot be 0. So 1 is a good candidate.

  @Override
  public void onCreate() {
      Log.d(mySrv, "Service created.");
   
      // Start Python in this process
      if (!Python.isStarted()) {
          Python.start(new AndroidPlatform(this));
      }  
      
      // Create notification channel
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          NotificationChannel serviceChannel = new NotificationChannel(
              ChannelID, "My Service Channel", NotificationManager.IMPORTANCE_DEFAULT);
          NotificationManager manager = getSystemService(NotificationManager.class);
          if (manager != null) { manager.createNotificationChannel(serviceChannel); }
      }
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Log.d(mySrv, "Service started.");
    
      // Put the service in a foreground state
      Intent notifIntent = new Intent(this, MainActivity.class);
      PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifIntent, PendingIntent.FLAG_IMMUTABLE);
      Notification notif = new NotificationCompat.Builder(this, ChannelID)
          .setContentTitle("My Service")
          .setContentText("Service is running")
          .setSmallIcon(R.mipmap.ic_launcher_round)
          .setContentIntent(pendingIntent)
          .build();
      startForeground(ONGOING_NOTIFICATION_ID, notif);        

      Log.d(mySrv, "Starting worker thread...");
      Python py = Python.getInstance();
      PyObject srv_thread = py.getModule("myapp.app");
      srv_thread.callAttr("StartServer");
      Log.d(mySrv, "Worker thread started.");

      Toast.makeText(this, "Service started !", Toast.LENGTH_SHORT).show();
      return START_STICKY;
  }

  @Override
  public void onDestroy() {
      Log.d(mySrv, "Stopping service thread...");
      Python py = Python.getInstance();
      PyObject srv_thread = py.getModule("myapp.app");
      srv_thread.callAttr("StopServer");
      Log.d(mySrv, "Service thread stopped.");
      
      Toast.makeText(this, "Service stopped !", Toast.LENGTH_SHORT).show();
      Log.d(mySrv, "Service destroyed.");
  }
  
  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }  
}

Solution

  • I studied all other questions about this problem and it seems that nothing works anymore, for the devices from our time (the year 2024)... Starting the service as foreground will still close it when you clear all recent apps. I don't like the solution with restart it by an alarm started in onDestroy event either, because it will interrupt my service and it's against common sense to brute force a service to restart... But I find an acceptable workaround:

    1. Settings > Apps > Manage Apps > Your App:
      • Pause app activity if unused: Off
      • App permisions > Pause app activity if unused: Off
      • Notifications > Show notifications: On
      • Battery saver: No restrictions
    2. Settings > Search: background autostart > Enable it for your app (it starts again the service in case it is stopped for low sistem resources.
    3. Start the app, go to the task manager screen, tap and hold on your app window until a button menu appears, and tap on the Lock button. In this mode, the app is ignored when you clear all recent apps and remains active. Of course, it's not nice to se it in the task manager, but you can sweep it away and it will close. And what is beautiful is that the service remains active ! You can dismiss the notification icon too, and now you have a real background service that stays alive !