我的编程空间,编程开发者的网络收藏夹
学习永远不晚

让Android应用不被杀死(killer)的方法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

让Android应用不被杀死(killer)的方法

方法:
对于一个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中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。由此带来三个问题:
    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 Service 服务不被杀死的妙招Android编程防止进程被第三方软件杀死的方法android PopupWindow 和 Activity弹出窗口实现方式android压力测试命令monkey详解android listview优化几种写法详细介绍Android 动画之ScaleAnimation应用详解android调试工具DDMS的使用详解Android的Activity跳转动画各种效果整理Android按钮单击事件的四种常用写法总结Android SQLite数据库增删改查操作的使用详解Android 杀死进程几种方法详细介绍


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

让Android应用不被杀死(killer)的方法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

让Android应用不被杀死(killer)的方法

方法: 对于一个service,可以首先把它设为在前台运行:代码如下: public void MyService.onCreate() { super.onCreate(); Notification no
2022-06-06

Android编程防止进程被第三方软件杀死的方法

本文实例讲述了Android编程防止进程被第三方软件杀死的方法。分享给大家供大家参考,具体如下: 项目测试的时候发现,按home键回到桌面,再用360清理内存,软件被结束,再次进入的时候报错,看了下log,以为是有的地方没有控制好,但是又不
2022-06-06

Linux杀不死的进程之CPU使用率700%解决方法

1. 问题发现[root@zwlbs3 ~]# topi. 发现有个进程CPU使用率居然700%,COMMAND 是一些随机的字符串组成,完了~ 中标了;第一想到就是“沙雕”它,kill 命令给我上。[root@zwlbs3 ~]# kil
2022-06-04

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录