How to always run a service in the background?
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 t开发者_JAVA技巧o a service. But I was not able to make this work.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.
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);
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>
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" />
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.
精彩评论