转载自: 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. 1211笔记关于//modal//更改窗口的根控制器//数据存取//Plist属性列表//-“沙盒机制”//plis属性列表//偏好设置//归档普通对象//联系人数据存储//协议与回调函数

    一.利用Modal形式展示控制器 1.如何展示// vc就是要展示的新控制器[self presentViewController:vc animated:YES completion:^{    N ...

  2. appium的使用

    这套教程年久失修,问题的人也比较多,于是,我重新整理了一套appium入门教程. appium新手入门(1)—— appium介绍 appium新手入门(2)—— 安装 Android SDK app ...

  3. Oracle存储过程学习笔记

    SQL是一种语言! SQL是一种语言! SQL是一种语言! 个人理解:存储过程就相当于Java中的方法;声明变量区域就相当于java中的声明局部变量一样,只是放到一个指定区域定义了 一.先看一部分基础 ...

  4. BAT 前端开发面经 —— 吐血总结

    更好阅读,请移步这里 聊之前 最近暑期实习招聘已经开始,个人目前参加了阿里的内推及腾讯和百度的实习生招聘,在此总结一下 一是备忘.总结提升,二是希望给大家一些参考 其他面试及基础相关可以参考其他博文: ...

  5. Codeforces Gym100952 A.Who is the winner? (2015 HIAST Collegiate Programming Contest)

      A. Who is the winner?   time limit per test 1 second memory limit per test 64 megabytes input stan ...

  6. C# 用程序读写另一个控制台程序

    一. using System; namespace ConsoleApp1 { class Program { static void Main(string[] args) { Console.W ...

  7. HDU 1017 A Mathematical Curiosity (输出格式,穷举)

    #include<stdio.h> int main() { int N; int n,m; int a,b; int cas; scanf("%d",&N); ...

  8. UVA Rails(stl-stack)

    具体思路是从B方向的序列 倒推 栈C 的入栈出栈顺序,  以B方向的5,4,3,2,1为例子:从一个数字5开始,要想从栈C拿到5号车厢,5号车厢得入栈C,且5号车厢处于栈顶位置,为了保证5号车  厢在 ...

  9. Unity防破解 —— 重新编译mono

        Unity4.x版本导出android包时,只能选择mono,无法使用il2cpp,这就造成了我们的程序集很容易被修改--很多朋友在发布项目时觉得即使代码暴露出去也没什么关系,只有项目火了才有 ...

  10. 23、Flask实战第23天:Flask-Restful

    Restful API规范 restful api是用于前端和后台进行通信的一套规范.使用这个规范可以让前后端开发变得更加轻松. 协议 采用http或者https 数据传输格式 数据之间传输的格式应该 ...