How to always run a service in the background?
Asked Answered
E

5

79

I am in the process of creating an app that is similar to the built-in SMS app.

What I need:

  • a service that is always running in the background
  • every 5 min. the service checks the current location of the device and calls a web service
  • if certain criteria are met, the service should generate a notification (just like the SMS app)
  • when the notification is clicked, the user is taken to the app (just like the SMS app)
  • when the app is installed the service should be started
  • when the device is rebooted, the service should be started

What I have tried:
- running a regular service which worked just fine until Android kills the service
- using the AlarmManager to make the 5 min. interval call to a service. But I was not able to make this work.

Evidential answered 2/4, 2010 at 11:23 Comment(0)
L
36

a service that is always running in the background

This is not possible in any real sense of the term, as you have discovered. It is also bad design.

every 5 min. the service checks the current location of the device and calls a web service

Use AlarmManager.

using the AlarmManager the make the 5 min. interval call to a service. But I was not able to make this work.

Here is a sample project showing how to use one, along with the use of WakefulIntentService so you stay awake while trying to do the whole Web service thing.

If you have continued problems with it, open up a new question about the specific things you are encountering with AlarmManager that are giving you grief.

Linzer answered 2/4, 2010 at 12:15 Comment(14)
i used this sample project and i put log and wait 10 minutes nothing is work ? i have to call anything service to start it ?Pelotas
@SamirMangroliya: If you look at the date on the answer, you will notice that it is over 3 years old. This example was written for Android 2.x, and you would need to reboot the device/emulator to get the alarms to start. Now, for Android 3.1+, even that is insufficient -- you need to run an activity before the on-boot receiver will be effective. I would recommend that you switch to github.com/commonsguy/cw-omnibus/tree/master/AlarmManager/… which is the same basic project, but is more up to date. Run it, and ensure the activity runs (shows a Toast), and the alarms will begin.Linzer
hello if i call PollReceiver.scheduleAlarms(this); in main activity and when i close app then restart(Suppose in one hour i open application more than 15 times) then its create alarm every time for 5 minutes ?Pelotas
@SamirMangroliya: Scheduling an alarm for an equivalent PendingIntent will cancel any existing alarm for that PendingIntent. Beyond that, please bear in mind that this is an example from a book, and it deliberately does not attempt to address all scenarios.Linzer
I have found the following post, that states this is possible: blogmobile.itude.com/2012/02/20/…. Is that approach working?Wigan
@MarkVincze: I have no idea what "this" is. The blog post you cite has nothing much to do with this SO question or my answer to it.Linzer
this: "a service that is always running in the background", about which you stated: "This is not possible in any real sense of the term". In the blog post they configure an app to be started in the background every time the phone is turned on. Am I misunderstanding something?Wigan
@MarkVincze: "Am I misunderstanding something?" -- yes. The blog post is about starting something at boot time. The question is about starting something at boot time (presumably) and having it run forever (hence, the word "always").Linzer
@Linzer can u pls tell me if i set alarm for every 4 hours using code and then i restart the device will the already set alarm need to reset again after reboot device because i want to start a service after every four hoursThacher
@ErumHannan: "will the already set alarm need to reset again after reboot" -- if you are using AlarmManager, yes, as the scheduled alarms are wiped out on a reboot.Linzer
I am using this sample but it skips loging on many devices like in Huawei it skips minutes and in oppo it stop logging after some time can you please help me with this @LinzerCe
@rana_sadam: This answer is from 2010. Quite a bit has changed since then, including the introduction of Doze mode and app standby on Android 6.0. Also, some device manufacturers have done their own thing with respect to power management (definitely Huawei, possibly Oppo). At this point, doing periodic work in the background will be very unreliable. Ideally, find some way to avoid it entirely.Linzer
Can you please guide me a way to do this I am stuck with this and unable to find any solution :( @LinzerCe
@rana_sadam: As I indicated in my previous comment, I do not know how to do periodic work in the background reliably.Linzer
C
26

One of my apps does something very similar. To wake the service after a given period I recommend postDelayed()

Have a handler field:

private final Handler handler = new Handler();

and a refresher Runnable

private final Runnable refresher = new Runnable() {
  public void run() {
    // some action
  }
};

You can fire your Notifications in the runnable.

On service construction, and after each execution start it like so:

handler.postDelayed(refresher, /* some delay in ms*/);

In onDestroy() remove the post

handler.removeCallbacks(refresher);

To start the service at boot time you need an auto starter. This goes in your manifest

<receiver android:name="com.example.ServiceAutoStarter">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
  </receiver>

and the ServiceAutoStarter looks like this:

public class ServiceAutoStarter extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    context.startService(new Intent(context, UpdateService.class));
  }
}

Stopping the OS from killing the service is tricky. Also your application can have a RuntimeException and crash, or your logic can stall.

In my case it seemed to help to always refresh the service on screen on with a BroadcastReceiver. So if the chain of updates gets stalled it will be resurrected as the user uses their phone.

In the service:

private BroadcastReceiver screenOnReceiver; 

In your service onCreate()

screenOnReceiver = new BroadcastReceiver() {

  @Override
  public void onReceive(Context context, Intent intent) {
    // Some action
  }
};

registerReceiver(screenOnReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));

Then unregister your service on onDestroy() with

unregisterReceiver(screenOnReceiver);
Crenelate answered 2/4, 2010 at 11:44 Comment(2)
I usually like your answers, but this one...not so much. :-( For example, you are proposing that the service register a receiver to resurrect itself once killed. The receiver will be killed along with the service; hence, this should have no effect. Moreover, the whole concept of an everlasting service is awful on Android and is the reason users are fighting back with task killers or the Running Services screen in the Settings app. There are very few cases where a truly everlasting service is needed -- the vast majority of the time, AlarmManager will suffice.Linzer
Thanks CWare, I should probably have made it clearer the resurrector is to protect against logic failure and the many things that can stall the chain of events that wake the service, as what is attributed to system shutting down the service can often be something else. I will look into the alarm manager approach, I vaguely recall trying this some time ago and not being able to get it to work.Crenelate
S
4

You can do this by some simple implementation:

public class LocationTrace extends Service implements LocationListener{

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
    private static final int TWO_MINUTES = 100 * 10;

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 10; // 30 seconds

    private Context context;

    double latitude;
    double longitude;

    Location location = null;
    boolean isGPSEnabled = false;
    boolean isNetworkEnabled = false;
    protected LocationManager locationManager;


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

        this.context = this;
        get_current_location();
//      Toast.makeText(context, "Lat"+latitude+"long"+longitude,Toast.LENGTH_SHORT).show();
        return START_STICKY;
    }


    @Override
    public void onLocationChanged(Location location) {
        if((location != null) && (location.getLatitude() != 0) && (location.getLongitude() != 0)){

            latitude = location.getLatitude();
            longitude = location.getLongitude();

            if (!Utils.getuserid(context).equalsIgnoreCase("")) {
                Double[] arr = { location.getLatitude(), location.getLongitude() };

               // DO ASYNCTASK
            }
        }

    }


    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(String provider) {

    }

    @Override
    public void onProviderDisabled(String provider) {

    }

    /*
    *  Get Current Location
    */
    public Location get_current_location(){

        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

        isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

        isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        if(!isGPSEnabled && !isNetworkEnabled){



        }else{
            if (isGPSEnabled) {

                if (location == null) {
                    locationManager.requestLocationUpdates(
                            LocationManager.GPS_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                    if (locationManager != null) {
                        location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
        //                  Toast.makeText(context, "Latgps"+latitude+"long"+longitude,Toast.LENGTH_SHORT).show();
                        }
                    }
                }
            }
            if (isNetworkEnabled) {

                locationManager.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER,
                        MIN_TIME_BW_UPDATES,
                        MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

                if (locationManager != null) {

                    if (location != null) {
                        latitude = location.getLatitude();
                        longitude = location.getLongitude();
        //              Toast.makeText(context, "Latgps1"+latitude+"long"+longitude,Toast.LENGTH_SHORT).show();
                    }
                }
            }
        }

        return location;
    }


    public double getLatitude() {
        if(location != null){
            latitude = location.getLatitude();
        }
        return latitude;
    }

    public double getLongitude() {
         if(location != null){
             longitude = location.getLongitude();
         }

        return longitude;
    }


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

    @Override
    public void onDestroy() {
        if(locationManager != null){
            locationManager.removeUpdates(this);
        }
        super.onDestroy();
    }

}

You can start service by this :

/*--Start Service--*/
startService(new Intent(Splash.this, LocationTrace.class));

In manifest:

 <service android:name=".LocationTrace">
            <intent-filter android:priority="1000">
                <action android:name="android.location.PROVIDERS_CHANGED"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
  </service>
Sclerotomy answered 31/12, 2015 at 8:55 Comment(0)
V
1

by these three steps, you can wake every 5 minutes most of the Android devices :

1. set your alternative AlarmManager for diffrent APIs :

AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(getApplicationContext(), OwnReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(), 0, i, 0);

if (Build.VERSION.SDK_INT >= 23) {
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}
else if (Build.VERSION.SDK_INT >= 19) {
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
}

2. build your own static BroadcastReceiver :

public static class OwnReceiver extends BroadcastReceiver {

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

       //do all of your jobs here

        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent i = new Intent(context, OwnReceiver.class);
        PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);

        if (Build.VERSION.SDK_INT >= 23) {
            am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
        }
        else if (Build.VERSION.SDK_INT >= 19) {
            am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
        } else {
            am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000 * 60 * 5), pi);
        }
    }
}

3. add <receiver> to AndroidManifest.xml :

<receiver android:name=".OwnReceiver"  />
Vatican answered 17/4, 2018 at 12:32 Comment(1)
Static receivers can't be initialized in the Manifest like this, rather you should use <receiver android:name="className$OwnReceiver" />Jordanjordana
P
1

According to me when you want your service to run always means that it shouldn't get stopped when the app is killed, because if your app is running or is in the background anyways your service will be running. When you kill the app while a service is running the onTaskRemoved function is triggered.

@Override
        public void onTaskRemoved(Intent rootIntent) {
            Log.d(TAG, "onTaskRemoved: removed");
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(System.currentTimeMillis() + 10000);
((AlarmManager) getSystemService(Context.ALARM_SERVICE)).setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), PendingIntent.getService(getApplicationContext(), 0, new Intent(getApplicationContext(), RegisterReceiverService.class), 0));
            super.onTaskRemoved(rootIntent);
        }

So basically once you kill the app the service will be started after 10 seconds. Note: In case you want to use

AlarmManager.ELAPSED_REALTIME_WAKEUP

the minimum time after which you'll be able to restart the service will be 15 minutes.

Remember you also need to start the service on reboot using BroadcastReceiver.

Piano answered 19/2, 2020 at 11:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.