Search code examples
androidandroid-servicealarmmanager

android alarm manager stops after few hours


I have create an alarm manager that start a service every 5 second, the app is running well but after few hours the alarm manager stop running. Note that the application is not used by the user which mean that it is running on background without user interaction. Does any one knows how to start a service every some second without stopping ?

AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
intent.putExtra(ONE_TIME, Boolean.TRUE); 
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi); 

Solution

  • Don't do such things with the alarm manager. The alarm manager is more a concept for tasks that should be done for a period of hours (in best cases).

    If you need a precise processing of your tasks, use a handler inside of a service, that post executes your runnable. Inside of your runnable, call the method to post execute the runnable again.

    Save the runnable and handler as member variables of the service and make the process sticky. If you want to stop the service and handler (handler won't stop if you just call stopService(Context) or Service.stopSelf(), you need to call removeCallbacks(runnable) with your runnable on the handler.

    Cheers.

    Btw: Think about if you really really want to start a service every 5 seconds. Maybe just do the Service work inside of the runnable of the service I just described.

    Edit2: If you need the Service to be reliable, add a BroadcastReceiver for Boot Actions, that will receive the Broadcast at boot and restart your service.

    Edit3: Code to check if your service is running, without starting it:

        public static boolean isRunning(Context context) {
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (YourService.class.getName().equals(service.service.getClassName())){
                return true;
            }
        }
        return false;
    }
    

    BUT, nevertheless, check in onStartCommand that your member 'handler' isn't null and ignore a start request if so, because this could lead you to a situation with multiple runnable processing.

    Edit4:

    Service Code Snippet:

    public class LoopingServiceSnipped extends Service{
    
    public static final long STANDARD_FREQUENCY = 1000;
    private static       long         loopingFrequency         = STANDARD_FREQUENCY;
    private Handler  serviceHandler; 
    private Runnable loop; 
    
    //EXTRAS
    
    private static final String       INTENT_EXTRA_STOP_SERVICE  = LoopingServiceSnipped.class.getSimpleName() + "_INTENT_EXTRA_STOP_SERVICE";
    private static final String       INTENT_EXTRA_FREQUENCY     = LoopingServiceSnipped.class.getSimpleName() + "_INTENT_EXTRA_FREQUENCY";
    
    /**
     * Determines if the service is running (reliably) without starting it.
     *
     * @param context
     *         needed for SystemServices
     *
     * @return true if the service is running
     */
    public static boolean isRunning(Context context) {
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (LoadingScreenAppDetectionService.class.getName().equals(service.service.getClassName())){
                return true;
            }
        }
        return false;
    }
    
    public static void stopService(Context c) {
        Intent i = new Intent(c, LoopingServiceSnipped 
        i.putExtra(INTENT_EXTRA_STOP_SERVICE, true);
        c.startService(i);
     }
    
    public static synchronized void setFrequency(Context c, long frequency) {
        Intent i = new Intent(c, LoadingScreenAppDetectionService.class);
        i.putExtra(INTENT_EXTRA_FREQUENCY, frequency);
        c.startService(i);
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
        init();
    }
    
    private void init() {
        //do your initialization here
    }
    
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (shouldStopService(intent)){
            LOG.d("Going to stop service!");
            tryStopService();
            return Service.START_NOT_STICKY;
        } else {
            return startOrContinueService(intent);
        }
    }
    
    private void tryStopService() {
        stopLooping();
        stopSelf();
    }
    
    private void stopLooping() {
        if (serviceHandler != null){
            serviceHandler.removeCallbacks(getLoop());
            serviceHandler = null;
        }
    }
    
    private int startOrContinueService(Intent intent) {
        if (hasNewFrequency(intent)){
            setFrequency(intent);
            stopLooping();
        }
        if (!isServiceRunning()){
         startLooping();
            }
        } else {
            LOG.d("Going to continue with service!");
        }
        return Service.START_STICKY;
    }
    
    
    private boolean hasNewFrequency(Intent intent) {
        return intent.hasExtra(INTENT_EXTRA_FREQUENCY);
    }
    
    private void setFrequency(Intent intent) {
        if (intent.hasExtra(INTENT_EXTRA_FREQUENCY)){
            loopingFrequency = intent.getLongExtra(INTENT_EXTRA_FREQUENCY, STANDARD_FREQUENCY);
        }
    }
    
    private boolean isServiceRunning() {
        return serviceHandler != null;
    }
    
    private boolean shouldStopService(Intent i) {
        if (i.hasExtra(INTENT_EXTRA_STOP_SERVICE)){
            return i.getBooleanExtra(INTENT_EXTRA_STOP_SERVICE, false);
        } else {
            return false;
        }
    }
    
    private void startLooping() {
        if (serviceHandler == null){
            serviceHandler = new Handler();
        }
        serviceHandler.postDelayed(getAppDetectionLoop(), getFrequency());
    }
    
    private Runnable getLoop() {
        if (loop == null){
            loop = new Runnable() {
                @Override
                public void run() {
                    //do your looping work here
                    startLooping();
                }
            };
        }
        return loop;
    }
    

    Additionally add Boot Receiver and Screen TurnOn Receiver.