Search code examples
androidnotificationsgarbage-collectionwakelock

Problem keeping App Running when Phone locks


So I am having an irritating problem. I am currently working on an application that is used to track user's sleep. We have a service that runs sending out sonar and listening to the returning waves. For this to work the service needs to run all night and because of some other issues we need the screen to lock. This service is started by an object that we pass an interface into to handle callbacks related to the processing. Currently the calling activity is implementing this interface to react properly to the callbacks.

Now the issue I am having is sometimes during the monitoring session, either the Service is being killed or the Application is being killed. To eliminate the possibility of being killed by the system I am looking for strategies to try to identify to the system that the app is running and needs to run all night even if the screen is locked.

First I tried a wake lock with a: PowerManager.PARTIAL_WAKE_LOCK

I recently added a tray notification for when the session is running to try and keep it alive but that has not worked out so well

public class Notification extends ContextWrapper {
    Context context;

    public Notification(Context base) {
        super(base);
        this.context = base;
        createChannels();
    }

    private NotificationManager mManager;

    public void createChannels() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationChannel sessionChannel = new NotificationChannel(Constants.Notification.TRACKING_CHANNEL,
                    Constants.Notification.ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW);
            sessionChannel.enableLights(false);
            sessionChannel.enableVibration(false);
            getManager().createNotificationChannel(sessionChannel);
            NotificationChannel  bedtimeChannel = new NotificationChannel(Constants.Notification.BEDTIME_CHANNEL,
                    Constants.Notification.ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
            getManager().createNotificationChannel(bedtimeChannel);

        }
    }

    public NotificationManager getManager() {
        if (mManager == null) {
            mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        }
        return mManager;
    }

    public NotificationCompat.Builder getTrackingChannelNotification(String title, String body) {
        Intent trackingIntent = new Intent(context, SessionRecordingActivity.class);
        trackingIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, trackingIntent, 0);

        return new NotificationCompat.Builder(getApplicationContext(), Constants.Notification.TRACKING_CHANNEL)
                .setContentTitle(title)
                .setContentText(body)
                .setContentIntent(pendingIntent)
                .setOngoing(true)
                .setSmallIcon(R.mipmap.ic_launcher_new);
    }

    public NotificationCompat.Builder getBedTimeChannelNotification(String title, String body, Intent actionIntent) {
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, actionIntent, 0);
        Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        return new NotificationCompat.Builder(getApplicationContext(), Constants.Notification.TRACKING_CHANNEL)
                .setSmallIcon(R.mipmap.ic_launcher_new)
                .setContentTitle(title)
                .setContentText(body)
                .setStyle(new NotificationCompat.BigTextStyle().bigText(body))
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(pendingIntent)
                .setSound(defaultSoundUri)
                .setAutoCancel(true);
    }
}

Here is what I do once the service has started:

NotificationCompat.Builder nb = mNotification.getTrackingChannelNotification(getString(R.string.tracking_in_progress), getString(R.string.sleep_well));
mNotification.getManager().notify(Constants.Notification.TRACKING_ID, nb.build());

And then I do this when the session is ended:

mNotification.getManager().cancel(Constants.Notification.TRACKING_ID);

So to my question: What else can I do to identify to the system that my app needs to keep running until the user ends it? (As a quick side note the Service is not started by my app directly it is a third party library starting the service).

EDIT: With further investigation it now seems that it may be my activity/application that is getting killed and not the service. Are there any other methods for keeping my application alive when the screen locks other than a wake lock or the tray notification?


Solution

  • You have to call startForeground in Oncreate() for the Service which you use Context.startForegroundService().

    @Override
    public void onCreate() {
        super.onCreate();
    
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);
    
            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
    
            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();
    
            startForeground(1, notification);
        }
    }
    

    You can find out more from this link