2014-01-21 16:45:02

我们知道,当进程长期不活动时,如果系统资源吃紧,会杀死一些Service,或不可见的Activity等所在的进程。

如何避免Service被系统杀死,随便在网上搜一下,都能搜到好几种方法,但是每一种方法都有不同的适用环境。

1. 添加android:persistent="true"

添加android:persistent="true"到AndroidManifest.xml,Google文档描述如下:

Whether or not the application should remain running at all times-true" if it should, and "false"if not. The default value is "false". Applications should not normally set this flag; persistence mode is intended only for certain system applications.可见这个属性不能随便用,到目前为止,我所发现使用该属性的应用只有Phone,而且使用是要求权限的,所以这个属性对第三方应用来说意义不是很大;

2. 设置onStartCommand()的返回值

这个思路比较有用,我们着重分析一下,该方法有四种返回值:

  1. START_STICKY
  2.  
  3. START_NOT_STICKY
  4.  
  5. START_REDELIVER_INTENT
  6.  
  7. START_STICKY_COMPATIBILITY

Google官方解释如下,有兴趣的可以展开看看:

  1. /**
  2. * Constant to return from {@link #onStartCommand}: compatibility
  3. * version of {@link #START_STICKY} that does not guarantee that
  4. * {@link #onStartCommand} will be called again after being killed.
  5. */
  6. public static final int START_STICKY_COMPATIBILITY = 0;
  7.  
  8. /**
  9. * Constant to return from {@link #onStartCommand}: if this service's
  10. * process is killed while it is started (after returning from
  11. * {@link #onStartCommand}), then leave it in the started state but
  12. * don't retain this delivered intent. Later the system will try to
  13. * re-create the service. Because it is in the started state, it will
  14. * guarantee to call {@link #onStartCommand} after creating the new
  15. * service instance; if there are not any pending start commands to be
  16. * delivered to the service, it will be called with a null intent
  17. * object, so you must take care to check for this.
  18. *
  19. * <p>This mode makes sense for things that will be explicitly started
  20. * and stopped to run for arbitrary periods of time, such as a service
  21. * performing background music playback.
  22. */
  23. public static final int START_STICKY = 1;
  24.  
  25. /**
  26. * Constant to return from {@link #onStartCommand}: if this service's
  27. * process is killed while it is started (after returning from
  28. * {@link #onStartCommand}), and there are no new start intents to
  29. * deliver to it, then take the service out of the started state and
  30. * don't recreate until a future explicit call to
  31. * {@link Context#startService Context.startService(Intent)}. The
  32. * service will not receive a {@link #onStartCommand(Intent, int, int)}
  33. * call with a null Intent because it will not be re-started if there
  34. * are no pending Intents to deliver.
  35. *
  36. * <p>This mode makes sense for things that want to do some work as a
  37. * result of being started, but can be stopped when under memory pressure
  38. * and will explicit start themselves again later to do more work. An
  39. * example of such a service would be one that polls for data from
  40. * a server: it could schedule an alarm to poll every N minutes by having
  41. * the alarm start its service. When its {@link #onStartCommand} is
  42. * called from the alarm, it schedules a new alarm for N minutes later,
  43. * and spawns a thread to do its networking. If its process is killed
  44. * while doing that check, the service will not be restarted until the
  45. * alarm goes off.
  46. */
  47. public static final int START_NOT_STICKY = 2;
  48.  
  49. /**
  50. * Constant to return from {@link #onStartCommand}: if this service's
  51. * process is killed while it is started (after returning from
  52. * {@link #onStartCommand}), then it will be scheduled for a restart
  53. * and the last delivered Intent re-delivered to it again via
  54. * {@link #onStartCommand}. This Intent will remain scheduled for
  55. * redelivery until the service calls {@link #stopSelf(int)} with the
  56. * start ID provided to {@link #onStartCommand}. The
  57. * service will not receive a {@link #onStartCommand(Intent, int, int)}
  58. * call with a null Intent because it will will only be re-started if
  59. * it is not finished processing all Intents sent to it (and any such
  60. * pending events will be delivered at the point of restart).
  61. */
  62. public static final int START_REDELIVER_INTENT = 3;

那么简单的说,四种模式的区别如下:

START_STICKY:kill后会被重启,但是重启后调用onStarfCommand()传进来的Intent参数为null,说明被kill的时候没有保存Intent;

START_STICKY_COMPATIBILITY:START_STICKY的兼容版,但是不能保证onStartCommand()方法被调用,如果应用程序的targetSdkVersion 小于 2.0版本,就会返回该值,否则返回START_STICKY,同时再次启动时只会调用onCreate(),不保证能调用onStartCommand()方法,代码如下:

  1. public int onStartCommand(Intent intent, int flags, int startId) {
  2. onStart(intent, startId);
  3. return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
  4. }
  5. =================================
  6. mStartCompatibility = getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.ECLAIR;
  7. =================================
  8. public static final int ECLAIR = 5; // 对应SDK2.0版本

START_NOT_STICKY:kill之后不会被重启;

START_REDELIVER_INTENT:kill后会被重启,同时重启调用onStartCommand()时再次传入保存的Intent。

启动一个service,然后在recent app里面杀死该进程,使用不同返回值时的log如下:

START_REDELIVER_INTENT

  1. D/PlayerService(16907): onCreate------
  2. D/PlayerService(16907): onStartCommand------and startId = 1
  3. D/PlayerService(16907): onStartCommand------and intent = Intent { cmp=com.example.bitmapfun/.ui.PlayerService }
  4. W/ActivityManager( 868): Scheduling restart of crashed service com.example.bitmapfun/.ui.PlayerService in 7776ms
  5. I/ActivityManager( 868): Start proc com.example.bitmapfun for service com.example.bitmapfun/.ui.PlayerService: pid=17271 uid=10153 gids={50153, 1028}
  6. D/PlayerService(17271): onCreate------
  7. D/PlayerService(17271): onStartCommand------and startId = 1
  8. D/PlayerService(17271): onStartCommand------and intent = Intent { cmp=com.example.bitmapfun/.ui.PlayerService }

被杀死的时候没有调用onDestory()方法,ActivityManager负责安排重启该service,此次重启大概需要7776ms,但这个时间不固定,有时很短,几秒,有时很长,可能要几十秒;

START_STICKY

  1. D/PlayerService(17620): onCreate------
  2. D/PlayerService(17620): onStartCommand------and startId = 1
  3. D/PlayerService(17620): onStartCommand------and intent = Intent { cmp=com.example.bitmapfun/.ui.PlayerService }
  4. W/ActivityManager( 868): Scheduling restart of crashed service com.example.bitmapfun/.ui.PlayerService in 5000ms
  5. I/ActivityManager( 868): Start proc com.example.bitmapfun for service com.example.bitmapfun/.ui.PlayerService: pid=18003 uid=10153 gids={50153, 1028}
  6. D/PlayerService(18003): onCreate------
  7. D/PlayerService(18003): onStartCommand------and startId = 3
  8. D/PlayerService(18003): onStartCommand------and intent = null

同上,不过传入的Intent为null,同时startId发生了变化,startId的官方解释是“A unique integer representing this specific request to start. Use with stopSelfResult(int)”,也就是说重启和第一次启动不是同一个request,也可以认为这是一个全新的request;

START_STICKY_COMPATIBILITY

  1. D/PlayerService(18177): onCreate------
  2. D/PlayerService(18177): onStartCommand------and startId = 1
  3. D/PlayerService(18177): onStartCommand------and intent = Intent { cmp=com.example.bitmapfun/.ui.PlayerService }
  4. W/ActivityManager( 868): Scheduling restart of crashed service com.example.bitmapfun/.ui.PlayerService in 5000ms
  5. I/ActivityManager( 868): Start proc com.example.bitmapfun for service com.example.bitmapfun/.ui.PlayerService: pid=18578 uid=10153 gids={50153, 1028}
  6. D/PlayerService(18578): onCreate------

这次重启根本就没有调用onStartCommand()方法;

START_NOT_STICKY

  1. D/PlayerService(19436): onCreate------
  2. D/PlayerService(19436): onStartCommand------and startId = 1
  3. D/PlayerService(19436): onStartCommand------and intent = Intent { cmp=com.example.bitmapfun/.ui.PlayerService }
  4. W/ActivityManager( 868): Scheduling restart of crashed service com.example.bitmapfun/.ui.PlayerService in 29285ms

没有再次启动被杀掉的service。

测试的代码很简单,大家可以自己尝试。现在有一个问题:我们该如何判断启动的service是正常启动还是杀死后被重启的,因为有时候我们需要知道这些信息,代码如下:

  1. private boolean isApplicationBroughtToBackground() {
  2. ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
  3. List<RunningTaskInfo> tasks = am.getRunningTasks(1);
  4. if (!tasks.isEmpty()) {
  5. ComponentName topActivity = tasks.get(0).topActivity;
  6. Log.d(TAG, "topActivity.getPackageName() = " + topActivity.getPackageName());
  7. if (!topActivity.getPackageName().equals(getPackageName())) {
  8. return true;
  9. }
  10. }
  11. return false;
  12. }

原理:service所在的activity和running task栈顶的activity做比较,因为一旦service所在的activity被杀死,那么系统会跳转到其他应用,如比桌面,或者SystemUI,或者用户可以打开的task栈中的其他TOP activity,此时的running task栈顶的activity肯定不是被杀死的activity了。

以上测试中所谓的杀死指的是在recent app里面或者Eclipse DDMS 点击Stop杀死,而不是在settings app info中“Force stop”,“Force stop”的log如下:

  1. D/PlayerService(21779): onCreate------
  2. D/PlayerService(21779): onStartCommand------and startId = 1
  3. D/PlayerService(21779): onStartCommand------and intent = Intent { cmp=com.example.bitmapfun/.ui.PlayerService }
  4. W/ActivityManager( 868): Scheduling restart of crashed service com.example.bitmapfun/.ui.PlayerService in 14898ms
  5. I/ActivityManager( 868): Force stopping service ServiceRecord{419a3198 u0 com.example.bitmapfun/.ui.PlayerService}

可以发现,虽然安排了启动,但是很快就被Force Stop了,这样也就失去了被重启的机会,至于在Settings中杀死进程的原理,有机会咱们展开讲。

3. startForeground()提高service的进程等级

我们知道Android进程分为5个等级:foreground process, visible process, Service process, background process, empty process,当系统资源吃紧的时候,会按照进程等级从低到高的顺序,同时根据进程消耗的资源从多到少的原则来kill一些进程,而service正处于第三个等级,如果能够提高service所在进程的等级,那么它被杀死的概率就会小一些。

可以利用Service的startForeground()方法将Service的进程等级从第三级提升到第一级foreground process。源代码如下:

  1. /**
  2. * Make this service run in the foreground, supplying the ongoing
  3. * notification to be shown to the user while in this state.
  4. * By default services are background, meaning that if the system needs to
  5. * kill them to reclaim more memory (such as to display a large page in a
  6. * web browser), they can be killed without too much harm. You can set this
  7. * flag if killing your service would be disruptive to the user, such as
  8. * if your service is performing background music playback, so the user
  9. * would notice if their music stopped playing.
  10. *
  11. * <p>If you need your application to run on platform versions prior to API
  12. * level 5, you can use the following model to call the the older setForeground()
  13. * or this modern method as appropriate:
  14. *
  15. * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
  16. * foreground_compatibility}
  17. *
  18. * @param id The identifier for this notification as per
  19. * {@link NotificationManager#notify(int, Notification)
  20. * NotificationManager.notify(int, Notification)}.
  21. * @param notification The Notification to be displayed.
  22. *
  23. * @see #stopForeground(boolean)
  24. */
  25. public final void startForeground(int id, Notification notification) {
  26. try {
  27. mActivityManager.setServiceForeground(
  28. new ComponentName(this, mClassName), mToken, id,
  29. notification, true);
  30. } catch (RemoteException ex) {
  31. }
  32. }

至于使用嘛,可以在在onCreate()或者onStartComman()方法中调用,然后可以在onDestroy()或者其他地方调用stopForeground(boolean removeNotification)方法来stop。

关于进程等级可访问:http://blog.csdn.net/llbupt/article/details/7358360

当然啦,网上还有一些其他的避免Service被杀死或者kill后重启的方法,比如监听android.intent.action.USER_PRESENT,来启动service,或者提高service IntentFilter的priority等,都能算是一些在某些特殊情况下可以其作用的方法,倒也不妨尝试一下。

还有人说用AlarmManager,如下:

  1. public void onReceive(Context context, Intent mintent) {
  2.  
  3. if (Intent.ACTION_BOOT_COMPLETED.equals(mintent.getAction())) {
  4. // 启动完成
  5. Intent intent = new Intent(context, Alarmreceiver.class);
  6. intent.setAction("arui.alarm.action");
  7. PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
  8. long firstime = SystemClock.elapsedRealtime();
  9. AlarmManager am = (AlarmManager) context
  10. .getSystemService(Context.ALARM_SERVICE);
  11.  
  12. // 10秒一个周期,不停的发送广播
  13. am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstime,
  14. 10 * 1000, sender);
  15. }
  16. }

监听系统启动的broadcast,然后每10秒一个周期,不停的发广播,这就是说应用一旦启动,就会不断的发广播,个人觉得这种方式不靠谱,原因如下:

1. 这样做无谓的操作,会消耗系统资源;

2. 一旦APP进程被杀死,怎么保证你的receiver不被杀死?

3. 不停的启动service,加入service中启动了其他的线程在做耗时的操作,这样做会产生大量的线程做重复的操作,即便service中没有启动其他线程,不断的调用onStartCommand()方法都不算是一个好办法。

当然了,如果实在没办法,必须得使用这种solution的话,我们可以判断service是否是alive,至于方法百度一下就有了。

至于有人说在onDestroy()中重启service,上面打出来的log大家也看到了,被kill的时候都没机会去调用onDestroy()。

如何提高Service的优先级避免被杀死或者杀死后如何再次重启Service?的更多相关文章

  1. Azure Service Bus(二)在NET Core 控制台中如何操作 Service Bus Queue

    一,引言 上一篇讲到关于 Azure ServiceBus 的一些概念,讲到 Azure Service Bus(服务总线),其实也叫 "云消息服务",是微软在Azure 上提供的 ...

  2. 提高Service提高进程优先级别,不被系统容易杀死

    1.首先要了解lowmemroykiller机制  在Android的lowmemroykiller机制中,会对于所有进程进行分类,对于每一类别的进程会有其oom_adj值的取值范围,oom_adj值 ...

  3. static关键字真能提高Bean的优先级吗?答:真能

    生命太短暂,不要去做一些根本没有人想要的东西.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习 ...

  4. MVC5.0 中如何提高Controller 的优先级

    //在area下建立的Home namespace WebApplication8.Areas.Weather.Controllers { public class HomeController : ...

  5. 在Salesforce中向外公布Service去创建Lead,并且用Asp.Net去调用此Service

    1):在Salesforce中如何配置,向外公布此Service,请看如下链接: http://www.shellblack.com/marketing/web-to-lead/ 2):如何在Asp. ...

  6. Apache CXF实现Web Service(4)——Tomcat容器和Spring实现JAX-RS(RESTful) web service

    准备 我们仍然使用 Apache CXF实现Web Service(2)——不借助重量级Web容器和Spring实现一个纯的JAX-RS(RESTful) web service 中的代码作为基础,并 ...

  7. service iptables xxx无效命令的情况下,如何启动/重启iptables

    最近在CentOS 7.6下使用service iptables xxx相关命令,提示如下错误:The service command supports only basic LSB actions ...

  8. lunix重启service network restart错误Job for network.service failed. See 'system 或Failed to start LSB: Bring

    1.mac地址不对 通过ip addr查看mac地址,然后修改cd /etc/sysconfig/network-scripts/目录下的文件里面的mac地址 2.通过以下方法 systemctl s ...

  9. Centos 7 使用(Service iptables stop/start)关闭/打开防火墙 Failed to stop iptables.service: Unit iptables.service not loaded.

    背景: 测试部署NetCore 项目到linux 系统时,窗口显示项目部署成功:但是本机无法访问(linux 在虚拟机上[ centos 7.6] );  如下图↓ 能够相互ping  通,(Xshe ...

随机推荐

  1. MVC视图中读取ViewBag传递过来的HashTable表数据

    视图中头部添加 @using System.Collections; 循环读取哈希表数据 <ul id="AccessView" class="sys_spec_t ...

  2. JSP-14- 常用集合类和接口

    List接口 List接口与实现类是容量可变的列表,可按索引访问集合中的元素,是有序的集合. Arraylist是以 array方式实现的List,允许快速随机存取,相当于LinkedList 不适合 ...

  3. 关于xfce中桌面没法显示回收站以及thunar中无法进行卷管理的解决办法

    出现这种问题的原因应该不是当前用户没在storage这个组里,因为我试过将用户从storage组里移除并不对影响桌面上回收站的显示. 问题的原因是没有安装gvfs这个软件,装上之后,重新登录当前用户, ...

  4. LNK1169: one or more multiply defined symbols found

    The build failed due to multiple definitions of one or more symbols. This error is preceded by error ...

  5. [Microsoft][ODBC 驱动程序管理器] 在指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配

    环境:  操作系统:64位WIN7   数据库:SQL Server 2000 SP1  开发语言:J2EE 在Servlet连接数据库时出错提示:  [Microsoft][ODBC 驱动程序管理器 ...

  6. 十分钟学会mysql数据库操作

    Part1:写在最前 MySQL安装的方式有三种: ①rpm包安装 ②二进制包安装 ③源码安装 这里我们推荐二进制包安装,无论从安装速度还是用于生产库安装环境来说,都是没问题的.现在生产库一般采用My ...

  7. wex5 教程之 图文讲解 Cloudx5一键部署

    视频教程地址:http://v.youku.com/v_show/id_XMTc3OTExNTUwNA==.html 效果预览: 一键部署cloudx5三要领 1.数据源命名为x5 2.数据库命名为x ...

  8. 关于C#中的new的用法

    修饰符:隐藏基类中的成员(是基类中的成员,所以字段.属性.事件等等都可以隐藏,不单单是方法哦) public class Car { public void WriteName(string name ...

  9. [问题2014S04] 解答

    [问题2014S04] 解答  由于 \(A\) 可对角化, 可设 \(\alpha_1,\alpha_2,\cdots,\alpha_n\in\mathbb{C}^n\) 是 \(A\) 的 \(n ...

  10. BW常用事务码Tcode

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...