Android Service 回收問題

最近需要寫一隻常駐的Service,但是印象中之前曾經有稍微眼尖看到,在記憶體不足的情況之下,Android會依照他特定的順序去回收資源,剛好Service又正好是Android回收清單的前幾名,既要常駐Service,那就是得要讓Service活得久一點。

How to restart a Service after getting Killed by apps like “Advanced Task Killer”?

這篇提到的方式是透過在onStartCommand回傳START_STICKY,讓Service被砍了之後,能夠自行重新開啟

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return START_STICKY;
}

Service API changes starting with Android 2.0

這篇有提到START_STICKYSTART_NOT_STICKYSTART_REDELIVER_INTENT的說明

The key part here is a new result code returned by the function, telling the system what it should do with the service if its process is killed while it is running:

說明
START_STICKY is basically the same as the previous behavior, where the service is left “started” and will later be restarted by the system. The only difference from previous versions of the platform is that it if it gets restarted because its process is killed, onStartCommand() will be called on the next instance of the service with a null Intent instead of not being called at all. Services that use this mode should always check for this case and deal with it appropriately.
START_NOT_STICKY says that, after returning from onStartCreated(), if the process is killed with no remaining start commands to deliver, then the service will be stopped instead of restarted. This makes a lot more sense for services that are intended to only run while executing commands sent to them. For example, a service may be started every 15 minutes from an alarm to poll some network state. If it gets killed while doing that work, it would be best to just let it be stopped and get started the next time the alarm fires.
START_REDELIVER_INTENT is like START_NOT_STICKY, except if the service’s process is killed before it calls stopSelf() for a given intent, that intent will be re-delivered to it until it completes (unless after some number of more tries it still can’t complete, at which point the system gives up). This is useful for services that are receiving commands of work to do, and want to make sure they do eventually complete the work for each command sent.

Android Service被系统回收的解决方法

這篇有提到增加優先權的方式:

  • 把service写成系统服务.
    add android:persistent="true" into the section in your AndroidManifest.xml
    在Manifest.xml文件中设置persistent属性为true,则可使该服务免受out-of-memory killer的影响。但是这种做法一定要谨慎,系统服务太多将严重影响系统的整体运行效率。
  • 提高service的优先级
    设置android:priority=”1000”
Manifest.xml(設定優先權)
1
2
3
4
5
<!-- 为了消去加上android:priority="1000"后出现的警告信息,可以设置android:exported属性,指示该服务是否能够被其他应用程序组件调用或跟它交互 -->
<service android:name="com.example.helloandroid.weatherforecast.service.UpdateWidgetService" android:exported="false" >
<!-- 为防止Service被系统回收,可以通过提高优先级解决,1000是最高优先级,数字越小,优先级越低 -->
<intent-filter android:priority="1000"></intent-filter>
</service>
  • 将服务写成前台服务foreground service
    重写onStartCommand方法,使用StartForeground(int,Notification)方法来启动service。
前景服務
1
2
3
4
5
6
7
8
9
10
Notification notification = new Notification(R.drawable.logo,"wf update service is running",System.currentTimeMillis());
pintent = PendingIntent.getService(this, 0, intent, 0);
notification.setLatestEventInfo(this, "WF Update Service","wf update service is running!", pintent);

//让该service前台运行,避免手机休眠时系统自动杀掉该服务
//如果 id 为 0 ,那么状态栏的 notification 将不会显示。
startForeground(startId, notification);

//同时,对于通过startForeground启动的service,onDestory方法中需要通过stopForeground(true)来取消前台运行状态。
//ps:如果service被杀后下次重启出错,可能是此时重发的Intent为null的缘故,可以通过修改onStartCommand方法的返回值来解决:
  • START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
  • START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
  • START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
  • START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

連結