转载自: http://blog.sina.com.cn/s/blog_3e3fcadd0100yjo2.html

参考:http://blog.csdn.net/windskier/article/details/6560925
http://blog.csdn.net/zmyde2010/article/details/6756368
http://blog.sina.com.cn/s/blog_514048cb0100wi2j.html

方法:
对于一个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文件:

 <application android:name="PhoneApp"
android:persistent="true"
android:label="@string/dialerIconLabel"
android:icon="@drawable/ic_launcher_phone">
...
</application>

设置后app提升为系统核心级别,任何情况下不会被kill掉, settings->applications里面也会屏蔽掉stop操作。

这样设置前的log:   Proc #19: adj=svc  /B 4067b028 255:com.xxx.xxx/10001 (started-services)

    # cat /proc/255/oom_adj
    4
设置后的log:  PERS #19: adj=core /F 406291f0 155:com.xxx.xxx/10001 (fixed)
    # cat /proc/155/oom_adj
     -12                # 这是CORE_SERVER_ADJ
注:init进程的oom_adj为-16(即SYSTEM_ADJ): cat  /proc/1/oom_adj
 
--------------------------------------------
Android相关部分分析:
在文件frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中有以下的代码:
final ProcessRecord addAppLocked(ApplicationInfo info) {
ProcessRecord app = getProcessRecordLocked(info.processName, info.uid); if (app == null) {
app = newProcessRecordLocked(null, info, null);
mProcessNames.put(info.processName, info.uid, app);
updateLruProcessLocked(app, true, true);
} if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))
== (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {
app.persistent = true;
app.maxAdj = CORE_SERVER_ADJ; // 这个常数值为-12。
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) <) {
mPersistentStartingProcesses.add(app);
startProcessLocked(app, "added application", app.processName);
} return app;
}

可见要想成为core service (即app.maxAdj = CORE_SERVER_ADJ(-12)),应用程序需要FLAG_SYSTEM和FLAG_PERSISTENT两个标志,FLAG_SYSTEM指的是应用位于/system/app下,FLAG_PERSISTENT就是指persistent属性。

而对于frameworks/base/services/java/com/android/server/SystemServer.java,则调用
       ActivityManagerService.setSystemProcess();
把自己的 app.maxAdj 设置成SYSTEM_ADJ,即-16。

-------------

原理:

Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。由此带来三个问题:
    1) 回收规则:  什么时候回收与回收哪一个?
    2) 避免误杀:  如何阻止被回收?
    3) 数据恢复与保存:  被回收了怎么办?
 
Android将进程分为6个等级,它们按优先级顺序由高到低依次是:
   1.前台进程( FOREGROUND_APP)
   2.可视进程(VISIBLE_APP )
   3. 次要服务进程(SECONDARY_SERVER )
   4.后台进程 (HIDDEN_APP)
   5.内容供应节点(CONTENT_PROVIDER)
   6.空进程(EMPTY_APP)

特征:
1.如果一个进程里面同时包含service和可视的activity,那么这个进程应该归于可视进程,而不是service进程。
2.另外,如果其他进程依赖于它的话,一个进程的等级可以提高。例如,一个A进程里的service被绑定到B进程里的组件上,进程A将总被认为至少和B进程一样重要。
3.系统中的phone服务被划分到前台进程而不是次要服务进程.

在android中,进程的oom_adj值也就代表了它的优先级。oom_adj值越高代表该进程优先级越低。文件/init.rc中有以下属性设置:
    setprop ro.FOREGROUND_APP_ADJ       0
    setprop ro.VISIBLE_APP_ADJ                     1
    setprop ro.SECONDARY_SERVER_ADJ   2
    setprop ro.HIDDEN_APP_MIN_ADJ           7
    setprop ro.CONTENT_PROVIDER_ADJ  14
    setprop ro.EMPTY_APP_ADJ                    15
/init.rc中,将PID为1的进程(init进程)的oom_adj设置为SYSTEM_ADJ(-16):
    # 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():
   1.首先移除package已被卸载的无用进程;
   2.基于进程当前状态,更新oom_adj值,然后进行以下操作:
         1) 移除没有activity在运行的进程;
         2) 如果AP已经保存了所有的activity状态,结束这个AP。
   3. 最后,如果目前还是有很多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 kernel中的low memory killer
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的几率变小:
  1) 提高进程的优先级:
        * 后台操作采用运行于前台的Service形式,因为一个运行着service的进程比一个运行着后台activity的等级高;
        * 按back键使得进程中的activity在后台运行而不是destory,需重载back按键(没有任何activity在运行的进程优先被杀).
        * 依赖于其他优先级高的进程;

2) 强制修改进程属性:
        * 在进程中设置:setPersistent(true);
        * 在Manifest文件中设置(如上)。

[Android Memory] 怎样使一个Android应用不被杀死的更多相关文章

  1. Android工程:引用另一个Android工程的方法详解

    本篇文章是对在Android中引用另一个Android工程的方法进行了详细的分析介绍.需要的朋友参考下   现在已经有了一个Android工程A.我们想扩展A的功能,但是不想在A的基础上做开发,于是新 ...

  2. 如何把一个android工程作为另外一个android工程的lib库

    http://zhidao.baidu.com/question/626166873330652844 一个工程包含另一个工程.相当于一个jar包的引用.但又不是jar包反而像个package 在网上 ...

  3. Android逆向 破解第一个Android程序

    这节正式开始破解编写的第一个Android工程,打开Android Killer,把第一节自己编写的Android apk拖入Android Killer. PS: 如果Android Killer不 ...

  4. 【Android实验】第一个Android程序与Activity生命周期

    目录 第一个Android程序和Activity生命周期 实验目的 实验要求 实验过程 1. 程序正常启动与关闭 2. 外来电话接入的情况 3. 外来短信接入的情况 4. 程序运行中切换到其他程序(比 ...

  5. Creating an Android Project(创建一个android项目)

    一个android项目包含了你的应用程序中的所有源代码文件,我们可以通过android sdk tools轻松地创建一个拥有默认文件跟文件夹的android项目. 这部分课程我们将展示两种创建andr ...

  6. 从零开始学android开发-创建第一个android项目

    打开ADT开发工具

  7. day01_使用Android Studio创建第一个Android项目

    使用Android Studio开发Android项目如此简单 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize ...

  8. Android开发自学笔记(Android Studio1.3.1)—2.开始第一个Android应用

    一.前言      使用Android Studio开发Android应用是一件非常简单的事情,因为它会帮你自动完成很多工作.本篇我们主要完成一个单击按钮在文本框显示当前时间的简单应用,借此来演示一下 ...

  9. 进入第一个Android应用界面

    前话 距离上次学习Android已经过去了半年了,这半年我干嘛去了? 嘛相信大家也没兴趣了解,简单来说就是我学习了周边的知识技术,最后终于转回Android. 感觉开发一个Android需要很多知识吧 ...

随机推荐

  1. 渗透测试中如何科学地使用V*P*N

    环境说明 Windows7 虚拟机,作为VPN网关,负责拨VPN.VPN可以直接OPENVPN,也可以使用ShadowSocks+SSTap. Kali 虚拟机, 渗透测试工作机 配置步骤 Windo ...

  2. linux系统备份脚本

    前言 之前写过<<linux系统简单备份的脚本>>, 最开始一直用着,后来觉得有必要改进下它,不管是从操作方式上还是脚本的工作方式上.之所以这么看重备份,是因为我经历过磁盘损坏 ...

  3. ARM内核全解析,从ARM7,ARM9到Cortex-A7,A8,A9,A12,A15到Cortex-A53,A57【转】

    转自:http://www.myir-tech.com/resource/448.asp 前不久ARM正式宣布推出新款ARMv8架构的Cortex-A50处理器系列产品,以此来扩大ARM在高性能与低功 ...

  4. Oracle 10g 安装环境配置脚本

    #!/bin/bash #Test in RHEL 5.5 for 10g c=`cat /etc/shadow | grep oracle | wc -l`if [ $c != 0 ]then  w ...

  5. 【 Linux 】为lnmp架构添加memcached支持

    一.首先搭建lnmp平台,这里不再演示.通过php页面来进行测试如下: [root@node1 ~]# vim /usr/local/nginx/html/info.php <?php $lin ...

  6. JavaScript获取和操作html的元素

    #转载请留言联系 1.获取元素 JavaScript的用途就是为了实现用户交互和网页的大部分动画.所以JavaScript常常需要操作html中的元素.要先操作就要先获取过来.JS有几种途径获取元素, ...

  7. 网站js埋点

    js埋点 1.埋点作用:  页面埋点的作用:其实就是用于流量分析.而流量的意思,包含了很多:页面浏览数(PV).独立访问者数量(UV).IP.页面停留时间.页面操作时间.页面访问次数.按钮点击次数.文 ...

  8. hdu 1147(线段相交)

    Pick-up sticks Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)To ...

  9. centos6.5 phpmyadmin 您应升级到 MySQL 5.5.0 或更高版本

    看到自己当初写的,并没有直接的解决问题,而是退而求其次,安装低版本的mysql5.1,然后安装对应版本的phpmyadmin 4.0.10.5 UnicodeDecodeError: 'ascii' ...

  10. UVA 11624 Fire!【两点BFS】

    Joe works in a maze. Unfortunately, portions of the maze have caught on fire, and the owner of the m ...