1

I'm doing an Android app that requires sending its location frequently, every 1 minute or 2 minutes at the most. For this, I use a JobSchedulerService. I've already managed to make it run more than once every 15 minutes on devices with Android N version by replacing the .setPeriodic() with a .setMinimumLatency(). The fact is that at the beginning it is executed periodically in the established time, but after a while it runs every 7 or 9 minutes approximately.

I have already included the application in the battery saving white list, but didn't work. Is there any way to execute it or a similar service every minute with no restrictions? Doesn't matter how much battery the app spends.

EDIT:

This is what I've tried:

ReceiverService:

public class ReceiverService extends WakefulBroadcastReceiver {

@Override
public void onReceive(Context ctx, Intent intent) {

    if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
        if (!isMyServiceRunning(ServiceBackground.class, ctx))
            startWakefulService(ctx, new Intent(ctx, ServiceBackground.class));

        new ServiceAlarmManager(ctx).register();
    }

}

private boolean isMyServiceRunning(Class<?> serviceClass,Context context) {
    ActivityManager manager = (ActivityManager)context. getSystemService(Context.ACTIVITY_SERVICE);
    for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if (serviceClass.getName().equals(service.service.getClassName())) {
            Log.i("Service already","running");
            return true;
        }
    }
    Log.i("Service not","running");
    return false;
}

}

The ServiceAlarmManager is exactly the same as @madking said.

smartgg
  • 23
  • 6
  • This is really awful practice. Let the device sleep, please. – Vyacheslav Nov 10 '17 at 13:53
  • what you want is not possible – Tim Nov 10 '17 at 13:53
  • It can't be impossible, I don't care if it is an awful practice because the device will be charging, the priority is to send location every minute, not the battery – smartgg Nov 10 '17 at 14:44
  • perhaps you have not noticed how google is putting more restrictions on what an app can do and how often with each update. But do tell me how to do it, since you seem to know something I don't – Tim Nov 10 '17 at 14:50
  • Of course i'm not an expert, from which I understood the new restrictions are for Android O, right? I think it has to be a way to do it, like a foreground service or something, but I don't know... – smartgg Nov 10 '17 at 15:15

2 Answers2

1

You can put your code that sends location in a Service and implement an AlarmManager that periodically checks if your Service is running and restarts it if the Service has been killed by OS. You'll have to implement the AlarmManager using a WakefulBroadcastReceiver.

ReceiverService.java

public class ReceiverService extends WakefulBroadcastReceiver {

@Override
public void onReceive(Context ctx, Intent intent) {

    if (!YourService.isRunning()) {
        startWakefulService(ctx, new Intent(ctx, YourService.class));
    }

    new ServiceAlarmManager(ctx).register();
}
}

ServiceAlarmManager.java

public class ServiceAlarmManager {

private Context ctx;
private static final int TIME_INTERVAL = 300 * 1000;

public ServiceAlarmManager(Context context) {
    ctx = context;
}

public void register() {

    Intent serviceRestarter = new Intent();
    serviceRestarter.setAction("someString");

    PendingIntent pendingIntentServiceRestarter = PendingIntent.getBroadcast(ctx, 0, serviceRestarter, 0);
    AlarmManager alarmManager = (AlarmManager) ctx.getSystemService(ctx.ALARM_SERVICE);
    Date now = new Date();
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, now.getTime() + TIME_INTERVAL, pendingIntentServiceRestarter);

}
}

Also register your BroadcastReceiver in your Manifest.xml file

<receiver android:name=".ReceiverService">
        <intent-filter>
            <action android:name="someString" />
        </intent-filter>
    </receiver>

The register() method does two things.

1- Issues a broadcast which is caught by WakefulBroadcastReceiver and restarts the Service if required

2- Sets the next alarm to be invoked to check if the Service has been killed.

This way the service keeps running even if the OS kills it and you'll be able to send location updates periodically.

Note: Though this practice is not recommended as your application will use more battery but you don't seem to care about it as I did not either as some business requirements don't leave us a choice.

madking
  • 595
  • 3
  • 12
  • Thank you for your answer! I'm trying to implement your code, but I don't know about `.isRunning()`, is it a Getter inside `MyService.class`? – smartgg Nov 10 '17 at 19:09
  • @smartgg No you'll have to implement something like this done in the first answer of this question. https://stackoverflow.com/questions/17588910/check-if-service-is-running-on-android – madking Nov 10 '17 at 20:00
  • So the `Service` which checks the `Location` and the `ServiceAlarmManager` are two different classes, right? – smartgg Nov 10 '17 at 20:27
  • I tried to implement your code and I also put it to start on boot. I made a reboot and it started, but just once :/ – smartgg Nov 10 '17 at 20:51
  • @smartgg Sorry for late reply as it was weekend ;) Yes they are two different classes. Your `Service` that checks the location should be extended from `IntentService`. Your second comment is rather unclear but it works, nothing else worked for me but this. Maybe you can share some code – madking Nov 13 '17 at 09:46
0

I tried this and it works: in the onCreate() of your activity you schedule an Alarm for every minute (setAlarm). Everytime the alarm is triggered, WakefulBroadcastReceiver is called, and that's where we launch our service(s):

private static long INTERVAL_ALARM = 1 * 60 * 1000;

public static void setAlarm(Context context) {
    long current_time = Calendar.getInstance().getTimeInMillis();
    Intent myAlarm = new Intent(context.getApplicationContext(), AlarmReceiver.class);
    PendingIntent recurringAlarm = PendingIntent.getBroadcast(context.getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
    AlarmManager alarms = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    alarms.setRepeating(AlarmManager.RTC_WAKEUP, current_time, INTERVAL_ALARM, recurringAlarm);
} 

And in the receiver:

public class AlarmReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
        Intent myService = new Intent(context, MyService.class);
        context.startService(myService);
    }

}

In your service, you should stopSeflf() in the end of your treatment.

Don't forget to register your BroadcastReceiver in your Manifest.xml file

NB: WakefulBroadcastReceiver is deprecated in API level 26.1.0. JobSchedulerService does the work

Meriam
  • 945
  • 8
  • 18