zoukankan      html  css  js  c++  java
  • Android下写一个永远不会被KILL掉的进程/服务

    Android 系统对于内存管理有自己的一套方法,为了保障系统有序稳定的运信,系统内部会自动分配,控制程序的内存使用。当系统觉得当前的资源非常有限的时候,为了保证一些优先级高的程序能运行,就会杀掉一些他认为不重要的程序或者服务来释放内存。这样就能保证真正对用户有用的程序仍然再运行。如果你的 Service 碰上了这种情况,多半会先被杀掉。但如果你增加 Service 的优先级就能让他多留一会,我们可以用 setForeground(true) 来设置 Service 的优先级。

    为什么是 foreground ? 默认启动的 Service 是被标记为 background,当前运行的 Activity 一般被标记为 foreground,也就是说你给 Service 设置了 foreground 那么他就和正在运行的 Activity 类似优先级得到了一定的提高。当让这并不能保证你得 Service 永远不被杀掉,只是提高了他的优先级。

    有一个方法可以给你更清晰的演示,进入 $SDK/tools 运行命令

    # adb shell dumpsys activity|grep oom_adj   
       
    Running Norm Proc # 6: oom_adj=  0 ProcessRecord{43635cf0 12689:com.roiding.netraffic/10028}   
    Running Norm Proc # 5: oom_adj=  7 ProcessRecord{436feda0 12729:com.android.browser/10006}   
    Running Norm Proc # 4: oom_adj=  8 ProcessRecord{4367e838 12761:android.process.acore/10016}   
    Running Norm Proc # 3: oom_adj=  8 ProcessRecord{43691cd8 12754:com.google.process.gapps/10000}   
    Running PERS Proc # 1: oom_adj=-12 ProcessRecord{43506750 5941:com.android.phone/1001}   
    Running PERS Proc # 0: oom_adj=-100 ProcessRecord{4348fde0 5908:system/1000}

    返回的一大堆东西,观察 oom_adj 的值,如果是大于 8 一般就是属于 backgroud 随时可能被干掉,数值越小证明优先级越高,被干掉的时间越晚。你看phone的程序是 -12 说明电话就是电话,其他什么都干了了,也的能接电话对吧。另外还有一个 -100 的,更邪乎因为是 system 如果他也完蛋了,你得系统也就挂了。

    我是天王盖地虎的分割线                                 

    从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文件:

    <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) < 0) {
                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中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。由此带来三个问题:

    • 回收规则:  什么时候回收与回收哪一个?
    • 避免误杀:  如何阻止被回收?
    • 数据恢复与保存:  被回收了怎么办?

    Android将进程分为6个等级,它们按优先级顺序由高到低依次是:

    • 前台进程( FOREGROUND_APP)
    • 可视进程(VISIBLE_APP )
    • 次要服务进程(SECONDARY_SERVER )
    • 后台进程 (HIDDEN_APP)
    • 内容供应节点(CONTENT_PROVIDER)
    • 空进程(EMPTY_APP)

    特征:

    • 如果一个进程里面同时包含service和可视的activity,那么这个进程应该归于可视进程,而不是service进程。
    • 另外,如果其他进程依赖于它的话,一个进程的等级可以提高。例如,一个A进程里的service被绑定到B进程里的组件上,进程A将总被认为至少和B进程一样重要。
    • 系统中的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():

    • 首先移除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 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的几率变小:

    • 提高进程的优先级:
      * 后台操作采用运行于前台的Service形式,因为一个运行着service的进程比一个运行着后台activity的等级高;
      * 按back键使得进程中的activity在后台运行而不是destory,需重载back按键(没有任何activity在运行的进程优先被杀).
      * 依赖于其他优先级高的进程;
    • 强制修改进程属性:
      * 在进程中设置:setPersistent(true);
      * 在Manifest文件中设置(如上)。
  • 相关阅读:
    jvm基本结构和解析
    多态的意思
    java中对象的简单解读
    double类型和int类型的区别
    python 解析xml文件
    win10不能映射Ubuntu共享文件
    Qt程序打包
    Ubuntu boot分区文件误删,系统无法启动,怎么解
    ubuntu Boot空间不够问题“The volume boot has only 5.1MB disk space remaining”
    Ubuntu 分辨率更改 xrandr Failed to get size of gamma for output default
  • 原文地址:https://www.cnblogs.com/yydcdut/p/3851029.html
Copyright © 2011-2022 走看看