转载自: 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. 不能使用联机NuGet 程序包

    打开菜单栏>>工具>>选项>>NuGet 程序包管理器>>选择程序包源 勾选上nuget.org,即可使用联机的Nuget包  

  2. CentOS 7 主机加固手册-上

    TIPs: 世界上有一撮人专门研究主机安全加固基线,有兴趣的读者可以到 http://benchmarks.cisecurity.org/ 获取更加详细专业的主机安全基线配置文档.或者到 https: ...

  3. linux中链表的使用【转】

    转自:http://blog.csdn.net/finewind/article/details/8074990 Linux下链表的使用方法跟我们常规的不一样,通常情况下,链表的next指针都指向节点 ...

  4. shell浅谈之三for、while、until循环【转】

    转自:http://blog.csdn.net/taiyang1987912/article/details/38929069 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[- ...

  5. SQLAlchemy中filter()和filter_by()有什么区别

    from:https://segmentfault.com/q/1010000000140472 filter: apply the given filtering criterion to a co ...

  6. [ python ] 使用sys模块实现进度条

    在写网络IO传输的时候, 有时候需要进度条来显示当前传输进度,使用 sys 模块就可以实现: sys.stdout.write() 这个函数在在控制台输出字符串不会带任何结尾,这就意味着这个输出还没有 ...

  7. bindingSource具体使用案例

    界面如下: using DevExpress.XtraBars.Docking; using DevExpress.XtraEditors; using NewPwrDY.DBEntity; usin ...

  8. JS / jquery 实现页面 面板拖动 QQ网页版登陆页面拖动

    参考:慕课网DOM实践探秘 http://www.imooc.com/learn/138 实现需求:点击页面头部,可以拖动面板.使用js原生和jquery 各实现一次. 可以学到:1.鼠标在当前页面的 ...

  9. C++的Public.lib(Public.dll) : fatal error LNK1112: module machine type 'X86' conflicts with target machine type 'x64'

    今天开始编译网游服务器,找前辈借来批处理文件,版本控制上拿下代码,库等一系列资源,尼玛啊,编译出错: Public.lib(Public.dll) : fatal error LNK1112: mod ...

  10. python 执行顺序

    从上往下顺序执行,定义的方法和类要写在调用之前, 如果有 if __name__ == '__main__'   改方法所在的文件作为启动文件时会被调用,如果作为模块被调用时不会被执行.