如何始终在后台运行服务?
Posted
技术标签:
【中文标题】如何始终在后台运行服务?【英文标题】:How to always run a service in the background? 【发布时间】:2011-02-03 17:06:33 【问题描述】:我正在创建一个类似于内置短信应用的应用。
我需要什么:
始终在后台运行的服务 每 5 分钟。该服务检查设备的当前位置并调用网络服务 如果满足某些条件,服务应生成通知(就像 SMS 应用程序一样) 当点击通知时,用户被带到应用程序(就像短信应用程序一样) 安装应用后,应启动服务 设备重启后,服务应该会启动我尝试过的: - 运行一个正常的服务,直到 android 终止该服务 - 使用 AlarmManager 制作 5 分钟。对服务的间隔调用。但我无法完成这项工作。
【问题讨论】:
【参考方案1】:始终在其中运行的服务 背景
正如您所发现的,这是not possible 在任何真正意义上的术语。也是bad design。
每 5 分钟。该服务检查 设备的当前位置和 调用网络服务
使用AlarmManager
。
使用 AlarmManager 制作 5 分钟。对服务的间隔调用。但是我 无法完成这项工作。
这里有一个sample project 展示了如何使用一个,以及WakefulIntentService
的使用,这样您在尝试做整个 Web 服务的事情时保持清醒。
如果您仍然遇到问题,请提出一个新问题,了解您在使用 AlarmManager
时遇到的让您感到悲伤的具体事情。
【讨论】:
我使用了这个示例项目,我输入了日志并等待 10 分钟,没有任何工作?我必须调用任何服务来启动它? @SamirMangroliya:如果您查看答案上的日期,您会发现它已经超过 3 岁。此示例是为 Android 2.x 编写的,您需要重新启动设备/模拟器才能启动警报。现在,对于 Android 3.1+,即使这还不够——您需要在启动接收器生效之前运行一个活动。我建议您切换到github.com/commonsguy/cw-omnibus/tree/master/AlarmManager/…,这是相同的基本项目,但更新的更多。运行它,并确保活动运行(显示Toast
),警报将开始。
你好,如果我打电话给 PollReceiver.scheduleAlarms(this);在主要活动中,当我关闭应用程序然后重新启动(假设在一小时内我打开应用程序超过 15 次)然后它每次创建警报 5 分钟?
@SamirMangroliya:为等效的PendingIntent
安排警报将取消该PendingIntent
的任何现有警报。除此之外,请记住,这是书中的一个例子,它故意不尝试解决所有场景。
我发现了以下帖子,说明这是可能的:blogmobile.itude.com/2012/02/20/…。这种方法有效吗?【参考方案2】:
我的一个应用程序做了非常相似的事情。要在给定时间后唤醒服务,我推荐postDelayed()
有一个处理程序字段:
private final Handler handler = new Handler();
还有一个复习Runnable
private final Runnable refresher = new Runnable()
public void run()
// some action
;
您可以在 runnable 中触发通知。
在服务构建时,每次执行后都像这样启动它:
handler.postDelayed(refresher, /* some delay in ms*/);
在onDestroy()
删除帖子
handler.removeCallbacks(refresher);
要在启动时启动服务,您需要一个自动启动器。这在您的清单中
<receiver android:name="com.example.ServiceAutoStarter">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
ServiceAutoStarter
看起来像这样:
public class ServiceAutoStarter extends BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
context.startService(new Intent(context, UpdateService.class));
阻止操作系统终止服务是很棘手的。此外,您的应用程序可能有 RuntimeException
并崩溃,或者您的逻辑可能会停止。
在我的情况下,总是使用BroadcastReceiver
刷新屏幕上的服务似乎很有帮助。因此,如果更新链停滞不前,它将在用户使用手机时重新启动。
在服务中:
private BroadcastReceiver screenOnReceiver;
为您服务onCreate()
screenOnReceiver = new BroadcastReceiver()
@Override
public void onReceive(Context context, Intent intent)
// Some action
;
registerReceiver(screenOnReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
然后在onDestroy()
上注销您的服务
unregisterReceiver(screenOnReceiver);
【讨论】:
我通常喜欢你的答案,但这个……不是那么多。 :-(例如,您建议服务注册一个接收器,以便在被杀死后复活。接收器将与服务一起被杀死;因此,这应该没有效果。此外,永久服务的整个概念很糟糕在 Android 上,这也是用户使用任务杀手或“设置”应用程序中的“运行服务”屏幕进行反击的原因。在极少数情况下需要真正持久的服务——在绝大多数情况下,AlarmManager
就足够了。
感谢 CWare,我可能应该更清楚地说明,resurrector 是为了防止逻辑故障和许多可以停止唤醒服务的事件链的事情,因为系统关闭服务通常可以是别的东西。我将研究警报管理器的方法,我隐约记得前段时间尝试过,但无法使其正常工作。【参考方案3】:
你可以通过一些简单的实现来做到这一点:
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();
您可以通过以下方式启动服务:
/*--Start Service--*/
startService(new Intent(Splash.this, LocationTrace.class));
在清单中:
<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>
【讨论】:
【参考方案4】:通过这三个步骤,您可以每 5 分钟唤醒大多数 Android 设备:
1.为不同的 API 设置您的替代 AlarmManager:
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。构建自己的静态广播接收器:
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.将<receiver>
添加到AndroidManifest.xml
:
<receiver android:name=".OwnReceiver" />
【讨论】:
静态接收器不能像这样在 Manifest 中初始化,你应该使用根据我的说法,当您希望您的服务运行时,始终意味着它不应在应用程序被终止时停止,因为如果您的应用程序正在运行或处于后台,那么您的服务将运行。当您在服务运行时终止应用时,会触发 onTaskRemoved 函数。
@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);
因此,基本上,一旦您终止应用程序,服务将在 10 秒后启动。 注意:如果你想使用
AlarmManager.ELAPSED_REALTIME_WAKEUP
您可以重新启动服务的最短时间为 15 分钟。
请记住,您还需要在重新启动时使用 BroadcastReceiver 启动服务。
【讨论】:
以上是关于如何始终在后台运行服务?的主要内容,如果未能解决你的问题,请参考以下文章