提高Service的优先级
工作中收音机后台运行的时候如果进行上网微信等操作可能造成收音机停掉。查看log发现是后台运行的服务被强行kill掉,原因是这款机子的内存本身较小。虽然Service本身的优先级已经很高,但在内存特别紧张的时候,还是会被系统回收内存机制回收掉,要想避免这种情况可以通过startForeground让服务前台运行,当stopservice的时候通过stopForeground去掉。
android中的进程
默认情况下, 同一个application中的所有component运行在同一个linux进程下. 启动一个component A时, 如果已存在处于运行状态中的component B, 且A和B属于同一个application, 那么component A将在component B所在的进程下运行. 否则将为A创建一个新的linux进程.
开发者也可以为application中的component指定不同的运行进程. manifest.xml文件中的<activity>, <service>, <receiver>, <provider>标签都支持android:process属性, 通过这个属性, 可以为component指定运行的进程. <application>标签也支持设定android:process属性, 用于为application下的所有component指定默认的运行进程.
进程优先级
当系统的内存不足时, android系统将根据进程优先级选择杀死一些不太重要的进程. 进程优先级从高到低分别为:
1. 前台进程. 以下的进程为前台进程:
a. 进程中包含处于前台的正与用户交互的activity;
b. 进程中包含与前台activity绑定的service;
c. 进程中包含调用了startForeground()方法的service;
d. 进程中包含正在执行onCreate(), onStart(), 或onDestroy()方法的service;
e. 进程中包含正在执行onReceive()方法的BroadcastReceiver.
系统中前台进程的数量很少, 前台进程几乎不会被杀死. 只有当内存低到无法保证所有的前台进程同时运行时才会选择杀死某个前台进程.
2. 可视进程. 以下进程为可视进程:
a. 进程中包含未处于前台但仍然可见的activity(调用了activity的onPause()方法, 但没有调用onStop()方法). 典型的情况是运行activity时弹出对话框, 此时的activity虽然不是前台activity, 但其仍然可见.
b. 进程中包含与可见activity绑定的service.
可视进程不会被系统杀死, 除非为了保证前台进程的运行而不得已为之.
3. 服务进程. 进程中包含已启动的service.
4. 后台进程. 进程中包含不可见的activity(onStop()方法调用后的activity). 后台进程不会直接影响用户体验, 为了保证前台进程/可视进程/服务进程的运行, 系统随时都有可能杀死一个后台进程. 一个正确的实现了生命周期方法的activity处于后台时被系统杀死, 可以在用户重新启动它时恢复之前的运行状态.
5. 空进程. 不包含任何处于活动状态的进程是一个空进程. 系统经常杀死空进程, 这不会造成任何影响. 空进程存在的唯一理由是为了缓存一些启动数据, 以便下次可以更快的启动.
进程优先级的额外说明
1. 系统会赋予进程尽可能高的优先级. 例如一个进程既包含已启动的service, 也包含前台activity, 则这个进程会被视为前台进程.
2. 由于组件之间的依赖性, 进程的优先级有可能被提高. 假如进程A服务于进程B, 则进程A的优先级不能低于进程B. 比如, 进程A的ContentProvider组件正在服务于进程B的某个组件, 或者进程A的service组件和进程B的某个组件绑定等, 这些情况下, 进程A的优先级都不会低于进程B(如果按照优先级规则, 进程A的优先级确实低于进程B, 则系统会选择提高进程A的优先级到和进程B相同).
3. 由于服务进程的优先级高于后台进程, 因此如果activity需要执行耗时操作, 最好还是启动一个service来完成. 当然, 在activity中启动子线程完成耗时操作也可以, 但是这样做的缺点在于, 一旦activity不再可见, activity所在的进程成为后台进程, 而内存不足时后台进程随时都有可能被系统杀死(但是启动service完成耗时操作会带来数据交互的问题, 比如耗时操作需要实时更新UI控件的状态的话, service就不是一个好的选择). 基于同样的考虑, 在BroadcastReceiver中也不应该执行耗时操作, 而应该启动service来完成(当然, BroadcastReceiver的生命周期过于短暂, 也决定了不能在其中执行耗时操作).
使用方法:
1. 在onStartCommand中或要进入后台启动Notification的时候调用startForeground。
2. 在停止收音机的函数或Service的onDestroy()回调中调用stopForeground。