先说点废话,年前在去佛吉亚好帮手面试当中遇到的其中一个问题就是,如果你的蓝牙或者canbus等等服务被kill掉了怎么处理的,这里涉及到一个service永存的概念,当时面试时脑子比较空白,但是回到工位之后这个问题其实是很简单的可以解决,记得当时是提及了一个用其它服务监控这些服务的做法,但是这其实都是不可靠的!因为监控服务也可能会被kill掉。下面会提及到。
那么要想永远不被系统kill掉,那你只能是参考系统应用(例如launcher,systemUI这些应用怎么杀,也是杀不死的)的做法,以下内容很多都是网上那些抄的,基本上就是这个原理
我这边做一个简短的总结:如果朋友你还是不能理解的话就继续往下阅读
1、置为system app
2、在AndroidManifest.xml文件中,加入android:persistent=”true” ;
3、启动时置为系统进程方式启动,而非用户进程
Android 系统对于内存管理有自己的一套方法,为了保障系统有序稳定的运信,系统内部会自动分配,控制程序的内存使用。当系统觉得当前的资源非常有限的时候,为了保证一些优先级高的程序能运行,就会杀掉一些他认为不重要的程序或者服务来释放内存。这样就能保证真正对用户有用的程序仍然再运行。如果你的 Service 碰上了这种情况,多半会先被杀掉。但如果你增加 Service 的优先级就能让他多留一会,我们可以用 setForeground(true) 来设置 Service 的优先级。
默认启动的 Service 是被标记为 background,当前运行的 Activity 一般被标记为 foreground,也就是说你给 Service 设置了 foreground 那么他就和正在运行的 Activity 类似优先级得到了一定的提高。当让这并不能保证你得 Service 永远不被杀掉,只是提高了他的优先级。
从Android 1.5开始,一个已启动的service可以调用startForeground(int, Notification)将service置为foreground状态,调用stopForeground(boolean)将service置为 background状态。
我们会在调用startForeground(int, Notification)传入参数notification,它会在状态栏里显示正在进行的foreground service。background service不会在状态栏里显示。
在Android 1.0中,将一个service置为foreground状态:
setForeground(true);
mNM.notify(id, notification);
将一个service置为background状态:
mNM.cancel(id);
setForeground(false);
对比看出,在1.0 API中调用setForeground(boolean)只是简单的改变service的状态,用户不会有任何觉察。新API中强制将 notification和改变service状态的动作绑定起来,foreground service会在状态栏显示,而background service不会
通过在androidmanifest.xml中的application标签中加入android:persistent=”true”属性后的确就能够达到保证该应用程序所在进程不会被LMK杀死。但有个前提就是应用程序必须是系统应用,也就是说应用程序不能采用通常的安装方式。必须将应用程序的apk包直接放到/system/app目录下。而且必须重启系统后才能生效。
除了一般的几种优先级外,还存在着coreserver,system这样的永远不会被LMK回收的优先级。系统中的电话应用就是coreserver优先级的。
通过查看源代码可以知道,只有应用程序的flag同时为FLAG_SYSTEM和FLAG_PERSISTENT时,才会被设置为coreserver优先级
if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))
== (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {
app.persistent = true;
app.maxAdj = CORE_SERVER_ADJ;
}
FLAG_SYSTEM在应用程序apk放在/system/app下时会被设置。所以才会出现只设置android:persistent=”true”仍然会被杀死的情况。
测试时发现,将应用程序放到/system/app后不重启系统,仍然会被识别为普通的进程。当系统重新启动时,会在一开始就启动该进程并把它优先级设置为coreserver。
通过dumpsys activity命令能够很明显的看出其中差别。
Running processes (most recent first):
App # 3: adj= 2/1 ProcessRecord{30858c20 1877:com.android.email/10014} (started-services)
PERS # 2: adj=-100/0 ProcessRecord{308fb390 1713:system/1000} (fixed)
App # 1: adj= 0/0 ProcessRecord{30908198 1794:android.process.acore/10005} (top-activity)
PERS # 0: adj= -12/0 ProcessRecord{3090d488 1789:xiao.xiong.test/10026} (fixed)
而且adj=-12时,这个进程通过ddms手动stop后会立即启动
对于一个service,可以首先把它设为在前台运行:
public void MyService.onCreate() {
super.onCreate();
Notification notification = new Notification(android.R.drawable.my_service_icon,”my_service_name”,System.currentTimeMillis());
PendingIntent p_intent = PendingIntent.getActivity(this, 0,
new Intent(this, MyMainActivity.class), 0);
notification.setLatestEventInfo(this, “MyServiceNotification, “MyServiceNotification is Running!”,p_intent);
Log.d(TAG, String.format(“notification = %s”, notification));
startForeground(0x1982, notification); // notification ID: 0x1982, you can name it as you will.
}
相较于/data/app下的应用,放在/system/app下的应用享受更多的特权,比如若在其Manifest.xml文件中设置persistent属性为true,则可使其免受out-of-memory killer的影响。
如应用程序’Phone’的AndroidManifest.xml文件:
Set init its forked children’s oom_adj.
write /proc/1/oom_adj –16
查看本机设置:
cat /sys/module/lowmemorykiller/parameters/adj
0,1,2,7,14,15
回收时,文件/init.rc中:
setprop ro.FOREGROUND_APP_MEM 1536 // 6M
setprop ro.VISIBLE_APP_MEM 2048 // 8M
setprop ro.SECONDARY_SERVER_MEM 4096 // 16M
setprop ro.HIDDEN_APP_MEM 5120 // 20M
setprop ro.CONTENT_PROVIDER_MEM 5632 // 22.4M
setprop ro.EMPTY_APP_MEM 6144 // 24M
这些数字也就是对应的内存阈值,一旦低于该值,Android便开始按顺序关闭相应等级的进程。
注意这些数字的单位是page: 1 page = 4 kB。所以上面的六个数字对应的就是(MB): 6,8,16,20,22,24。
查看现在的内存阈值设置:
cat /sys/module/lowmemorykiller/parameters/minfree
要想重新设置该值(对应不同的需求):
echo “1536,2048,4096,5120,15360,23040”>/sys/module/lowmemorykiller/parameters/minfree
这样当可用内存低于90MB的时候便开始杀死”空进程”,而当可用内存低于60MB的时候才开始杀死”内容供应节点”类进程。
具体的回收实现在ActivityManagerService.java中的函数trimApplications():
首先移除package已被卸载的无用进程;
基于进程当前状态,更新oom_adj值,然后进行以下操作:
1) 移除没有activity在运行的进程;
2) 如果AP已经保存了所有的activity状态,结束这个AP。
最后,如果目前还是有很多activities 在运行,那么移除那些activity状态已经保存好的activity。
更新oom_adj的值:
在ActivityManagerService.java文件的ComputeOomAdjLocked() 中计算出进程的oom_adj,例如:
if (app == TOP_APP) {
// The last app on the list is the foreground app.
adj = FOREGROUND_APP_ADJ;
app.adjType = “top-activity”;
}
Android的Low Memory Killer根据需要(当系统内存短缺时)杀死进程释放其内存,源代码在kernel/drivers/misc/lowmemorykiller.c中。简单说,就是寻找一个最合适的进程杀死,从而释放它占用的内存。
最合适的进程是:
oom_adj越大
占用物理内存越多
一旦一个进程被选中,内核会发送SIGKILL信号将之杀死:
复制代码
for_each_process(p) {
……
if(selected == NULL || p->oomkilladj > selected->oomkilladj ||
(p->oomkilladj == selected->oomkilladj && tasksize > selected_tasksize))
{
selected = p;
}
}
if(selected != NULL) {
force_sig(SIGKILL, selected);
}
复制代码
查看LRU列表:adb shell dumpsys activity
当activitydemo在前台时:
包含Service的进程的优先级比较高,在computeOomAdjLocked中将其分为了两小类:
复制代码
static final int MAX_SERVICE_INACTIVITY = 30*60*1000;
if (now < (s.lastActivity+MAX_SERVICE_INACTIVITY)) {
if (adj > SECONDARY_SERVER_ADJ) {
adj = SECONDARY_SERVER_ADJ;
app.adjType = “started-services”;
app.hidden = false;
}
}
if (adj > SECONDARY_SERVER_ADJ) {
app.adjType = “started-bg-services”;
}
复制代码
完全让进程不被kill是不可能的,我们可以通过一些操作,使进程被kill的几率变小:
提高进程的优先级:
* 后台操作采用运行于前台的Service形式,因为一个运行着service的进程比一个运行着后台activity的等级高;
* 按back键使得进程中的activity在后台运行而不是destory,需重载back按键(没有任何activity在运行的进程优先被杀).
* 依赖于其他优先级高的进程;
强制修改进程属性:
* 在进程中设置:setPersistent(true);
* 在Manifest文件中设置(如上)。
。
如果你写的不是系统应用,只能说,在很大概率上不被杀死。也就是说也不是%100 永远不被 Kill.
说一下我的思路吧
1,定义一个服务在后台运行,让这个服务单独一个进程,不要和你的应用程序共享一个进程。
2.在你的service的onDestroy()方法中调用 onStart() 方法。
3.定义一个接口开机广播的接收器,开机就启动你的服务。(可以通过监听开机消息实现)
4.当然杀不死,主要的功能就是实现网络连接时候,数据要发送到服务器,这个时候,就需要网络改变监听。
网络状态发生变化的时候,系统会发出 android.net.conn.CONNECTIVITY_CHANGE .
我们在监听这个消息,然后进行相应的处理就可以了。
进程,优先级从高到低依次是:
1. 前台进程(Foreground Process)
2. 可视进程(Visible Process)
3. 服务进程(Service Process)
4. 后台进程(Background Process)
5. 空进程(Empty Process)
前台进程(Foreground Process)是指当前的活动进程,即那些有窗口、控件的,正与用户进行交互的应用程序进程。Android会努力尝试从其它进程回收系统资源来保持其响应。
2. 可见进程(Visible Process)是指那些可见的活动进程,但是当前它不在前台运行,不能对用户事件作出反映。比如一个程序A正在前台运行,接着又启动了另一个程序B,程序B的窗口把程序A的窗口完全或部分遮挡,那么,程序A现在就属于可见进程了。一般来说,只有在系统资源十分缺乏的情况下,为了保证活动进程的运行,系统才会终止它。
3. 服务进程(Service Process)是指那些没有可见界面的进程,它们不能直接与用户进行交互。前台进程(通常是一个Activity)变为非活动时,会暂停运行;但服务进程可在后台保持运行,因此如果要保持进程在转到后台后也能运行,就必需启动一个Service。但是一旦前台进程需要资源时,Service就很容易被系统给kill掉。通常,默认情况下Service被kill掉后会自动重启,但是重启后并不能保证恢复先前的环境;如果不需要Service自动重启,调用stopSelf()函数即可。
4. 后台进程(Background Process)是指不可见的,且没有任何正在运行的服务的活动的进程。通常后台进程会比较多,Android将按“最后一个被看到,第一个终止”的方式来终止它们,从而为前台进程提供资源。
5. 对于空进程,Android为了提高系统的整体性能,经常在在应用程序的生存期结束后仍然把它们保存在内存中,当该程序被再次启动时,可以加快启动速度。这类进程会根据需要被定期终止。
由上可知,要想自己所写的Service不轻易被系统在回收资源时kill掉,正常的做法是尽量提高程序的优先级,如调用startForeground(true)。因为Service启动时默认被标记为Background,当前运行的Activity被标记为Foreground,当将Service设置为Foreground后,它的优先级就与正在运行的Activity类似,但是这也不能保证它不被系统kill。
如果我们要写一个监控程序,监测其它程序的运行,那么该怎么办呢?我们必需保证我们的程序不会因回收资源而kill。显然,优先级为2-5的进程都不可靠,作为监控程序,其优先级也不可能为1。
这样看上去,貌似没有办法了……别急,继续看下面……
解决方法:
简单地说,就是将自己写的Service变成Core Service,即将自己写的程序提升为系统级别的程序,这样在任何情况下它都不会被kill掉。
要想自己写的Service提升为Core Service,应用程序需要设置两个标志:FLAG_PERSISTENT和FLAG_SYSTEM。
具体做法如下:
1. 设置FLAG_PERSISTENT:在AndroidManifest.xml文件中,加入android:persistent=”true” ;
- 设置FLAG_SYSTEM:将你写的应用程序放到/system/app/目录下即可。具体操作如下:
A. 连接adb,然后执行如下两条命令:
adb remount
adb push your.apk /system/app/
命令adb remount是为了获取系统目录的临时操作权限,然后使用push命令将自己写的程序包上传到系统应用程序目录中。
B. 重启手机。开机后,在应用列表中就可以看到你写的程序了。
需要说明的是:系统程序不能像非系统程序那样使用adb install your.apk来安装apk包,我们只需要将apk包拷贝到/system/app/目录下重启即可。如果使用adb install的方式,那么FLAG_SYSTEM标志不会被设置,FLAG_PERSISTENT标志也将不起任何作用。
在用eclipse下:
开发Android程序时,有时候在程序运行的时候,不能让系统休眠,否则有一些运行会停止,因此我们需要设置禁止休眠,有两种方式:一种是添加权限,别一种是代码中设置,建议使用第一种方式,这样,在安装程序的时候会进行提醒:
第一种方式:
在Manifest.xml文件里面用user-permission声明
名称为:android.permission.WAKE_LOCK
第二种方式:
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
把这段代码加在setContentView(R.layout.main)之前即可