0

I'm using WorkManager to keep my foreground service running even after my app is killed. When I run my app, WorkManager starts my service immediately. But when I kill my app, WorkManager takes ~3 minutes to starts my service. I can't afford any delay, as my service has a broadcast receiver to receive SMS, and my functionality requires that I don't miss even a single SMS - I might miss some SMS if my work manager starts my foreground service after a delay.

How do I make work manager start my service immediately after killing the app?

This is the code I'm using for WorkManager:

public class DemoSyncJob extends Worker {

    public static final String TAG = ">>>> job_demo_tag";

    public DemoSyncJob(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @Override
    @NonNull
    public Worker.Result doWork() {
        // run your job here
        Log.d(TAG, "onRunJob: ");
        if(!isMyServiceRunning(getApplicationContext(), MessageReceiverService.class)){
            Intent intent=new Intent(getApplicationContext(),MessageReceiverService.class);
            getApplicationContext().startService(intent);
        }
        scheduleJob();
        return Worker.Result.success();
    }

    public static WorkRequest scheduleJob() {
        return new OneTimeWorkRequest.Builder(DemoSyncJob.class)
                .build();

    }

    public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
        try {
            ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
                if (serviceClass.getName().equals(service.service.getClassName())) {
                    return true;
                }
            }
        }catch (Exception e){
            Log.e(TAG, "isMyServiceRunning: ",e );
        }
        return false;
    }
}

I'm using the following version of WorkManager:

implementation 'androidx.work:work-runtime:2.4.0'

And this:

 implementation "com.google.android.gms:play-services-gcm:17.0.0"

This is the code for MessengeReceiverService.java:

public class MessageReceiverService extends Service
{
    private static BroadcastReceiver br_ScreenOffReceiver;
    private static MessageListener mListener;
    SharedPreferences sharedPreferences;
    SharedPreferences.Editor myEdit;
    public static final String CHANNEL_ID = "ForegroundServiceChannel";


    @Override
    public IBinder onBind(Intent arg0)
    {
        return null;
    }

    @Override
    public void onCreate()
    {
        createNotificationChannel();
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,
                0, notificationIntent, 0);
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Foreground Service")
                .setContentText("Hi, Sparsh Contento Dutta here")
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentIntent(pendingIntent)
                .build();
        startForeground(100, notification);

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        registerScreenOffReceiver();


        return START_STICKY;
    }



    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel serviceChannel = new NotificationChannel(
                    "100",
                    "Foreground Service Channel",
                    NotificationManager.IMPORTANCE_DEFAULT
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(serviceChannel);
        }
    }

    @Override
    public void onDestroy()
    {
        unregisterReceiver(br_ScreenOffReceiver);
    }


    private void registerScreenOffReceiver()
    {
        br_ScreenOffReceiver = new BroadcastReceiver()
        {
            @Override
            public void onReceive(Context context, Intent intent) {
                Toast.makeText(context, "Entered onReceive()", Toast.LENGTH_LONG).show();
                Log.i(TAG, "Entered onReceive");
                sharedPreferences= context.getSharedPreferences("MySharedPref", MODE_PRIVATE);
                myEdit=sharedPreferences.edit();
                Bundle data = intent.getExtras();
                Object[] pdus = (Object[]) data.get("pdus");
                String recMsgString = "";

                for(int i=0; i<pdus.length; i++){
                    SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
                    byte[] userData = smsMessage.getUserData();
                    if (userData!=null){
                        for(int index=0; index<userData.length; ++index)
                        {
                            recMsgString += Character.toString((char)userData[index]);
                        }
                    }
                     String message =
                             "Service center address: " + smsMessage.getServiceCenterAddress()+
                                     "\nPseudo object: " + smsMessage.getPseudoSubject() +
                             "\nDisplay originating address : " + smsMessage.getDisplayOriginatingAddress()
                            + "\nOriginating address : " + smsMessage.getOriginatingAddress()
                            +"\nUser data : " + recMsgString
                            + "\nEmail From: " + smsMessage.getEmailFrom()
                            + "\nEmail Body: " + smsMessage.getEmailBody()
                            + "\nDisplay message body: " + smsMessage.getDisplayMessageBody()
                            + "\nTime in millisecond: " + smsMessage.getTimestampMillis()
                            + "\nMessage: " + smsMessage.getMessageBody();


                    if (mListener!=null)
                    mListener.messageReceived(message);

                    myEdit.putString("message", message);
                    myEdit.apply();
                }
            }

        };

        IntentFilter filter = new IntentFilter();
        filter.addAction("android.provider.Telephony.SMS_RECEIVED");
        filter.addCategory(Intent.CATEGORY_DEFAULT);
        registerReceiver(br_ScreenOffReceiver, filter);
    }

    public static void bindListener(MessageListener listener){
        mListener = listener;
    }

}

How do I solve this issue?

Spires
  • 35
  • 6

1 Answers1

0

Firstly as per Android docs of WorkManger

WorkManager is not intended for in-process background work that can safely be terminated if the app process goes away or for work that requires immediate execution.

In some Android devices the WorkManger would not run if the app is removed from task manager because they don't have Stock Android and the WorkManager would resume only once the app is relaunched. So using WorkManager is not the best solution for your use case.

Instead you could have a implicit broadcast receiver to read the incoming SMS. Something like this

If you want to do trigger a service to do some other stuff you need to use the following

startForegroundService(intent) for Android Oreo onwards.
startService(intent) for Pre-Oreo Android version.

Alternatively you could simply use the following, this handles the Pre & Post Oreo

ContextCompat.startForegroundService(getContext(), serviceIntent);

For your service to run even when the app is killed (app is not in foreground or app is removed from task manager) then you need to create a notification and call the following method within a time window of 5 secs else the system will kill your service. This should be done in onStartCommand as the onCreate would be called once. This rule applies if your app targets API level 26 or higher because of background restrictions.

startForeground(id, notification)

Lastly don't forget to stop your service.

akhil nair
  • 1,371
  • 1
  • 11
  • 19