进程保活(一)

Android进程保活主要包括两个方面:
1.提高进程优先级,降低进程被杀死的概率
2.在进程被杀死后,进行拉活。
本文主要讲解第一点如何提高进程优先级。

1.进程的优先级

Android系统会尽力保持应用的进程,但是有时为了给新的进程和更重要的进程回收一些内存空间,会移除一些旧的进程。
为了决定哪些进程留下,哪些进程被杀死,系统根据子啊进程中运行的组件和组件的状态,为每个进程分配了一个优先级等级。优先级最低的进程首先被杀死。
系统根据进程的重要性,划分5级:

  1. 前台进程(Foreground process)
  2. 可见进程(Visible process)
  3. 服务进程(Service process)
  4. 后台进程(Background process)
  5. 空进程(Empty process)
    在这里插入图片描述
    前台进程的重要行最高,依次递减,空进程的重要行最低,下面分别来阐述每种级别的进程。

1.1 前台进程—Foreground process

用户当前操作所必需的进程。通常在任意给定时间前台进程都为数不多。只有当内存不足以支持它们同时继续运行这一万不得已的请开给你下,系统才会终止它们。

主要场景:
A.拥有用户正在交互的Activity(已调用onResume())
B.拥有某个Service,后者绑定到用户正在交互的Activity
C.拥有正在前台运行的Service(服务已调用startForeground())
D.拥有正执行一个生命周期回调的Service(onCreate(),onStart()或onDestory())
E.拥有正执行其onReceive()方法的BroadcastReceiver.

1.2 可见进程—Visible process

没有任何前台组件,但仍会影响用户在屏幕上所见内容的进程。可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。

主要场景:
A.拥有不在前台,但仍对用户可见的Activity(已调用onPause())
B.拥有绑定到可见(或前台)Activity的Service

1.3 服务进程— Service process

尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。

主要场景:
A.正在运行startService()方法启动的服务,且不属于上述两个更高类别进程的进程。

1.4 后台进程—Background process

后台进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。通常会有很多后台进程在运行,因此它们会保存在LRU列表中,以确保包含用户最近查看的Activity的进程最后一个被终止。如果某个Activity正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航会该Activity时,Activity会恢复其所有可见状态。

主要场景:
A.对用户不可见的Activity(已调用Activity的onStop()方法)

1.5 空进程—Empty process

保存这种进程的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。

主要场景:
A.不含任何活动应用组件的进程。

2.进程回收策略

2.1 内存阈值

系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app,这套杀进程回收内存的机制叫做Low Memory Killer。那这个不足是怎么来规定呢,那就是内存阈值,我们可以使用cat /sys/module/lowmemorykiller/parameters/minfree来查看某个手机的内存阈值。内存阈值在不同的手机上不一样,一旦低于该值,Android便开始按顺序关闭进程。因此Android开始结束优先级最低的空进程。你或许有一个疑问,假设现在内存不足,空进程都被杀光了,现在要杀后台进程,但是手机中后台进程很多,难道要一次性全部都清理掉?当然不是,进程是有它的优先级的,这个优先级通过进程的adj值来反映,它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收,adj值定义在com.android.server.am.ProcessList类中。oom_adj的值越小,进程的优先级越高,普通进程的oom_adj值是大于等于0的,而系统进程oom_adj的值是小于0的,我们可以通过cat /proc/进程id/oom_adj看到当前进程的adj值。

应用运行中并且在前台时,看到adj值是0,0就代表这个进程是属于前台进程,按下Back键后,将应用至于后台,再次查看,adj值变成了8,8代表这个进程是属于不活跃的进程。

关于oom_adj的说明如下表所示:
在这里插入图片描述
其中红色部分代表比较容易被杀死的Android进程(oom_adj>=4),绿色部分表示不容易被杀死的Android进程,其他表示非Android进程(纯Linux进程)。在Lowmemorykiller回收内存时会根据进程的级别优先杀死oom_adj比较大的进程,对于优先级相同的进程则进一步受到进程所占内存和进程存活时间的影响。

Android手机中进程被杀死可能有如下情况:
在这里插入图片描述
综上,可以得出减少进程被杀死概率无非就是想办法提高进程优先级,减少进程在内存不足等情况下被杀死的概率。

3.提高进程优先级的方案

3.1开启一个像素的Activity

基本思想:
由于系统一般是不会杀死前台进程的,所以要使得进程常驻,我们只需要在锁屏的时候在本进程开启一个Activity,为了欺骗用户,让这个Activity的大小是1像素,并且透明吴东华,在开屏幕的时候,把这个Activity关闭掉,所以这个就需要监听系统锁屏广播。通过这个方案,可以是进程的优先级在屏幕锁屏时间有4提升为1。
本方案主要解决第三方应用及系统管理工具在检测到锁屏事件后一段时间(一般是5分钟以内)会杀死后台进程,以达到省电的目的问题。

具体实现:
首先定义Activity,并设置Activity的大小为1像素。

public class KeepAliveActivity extends AppCompatActivity {
    private static final StringTAG="keeplive";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
        Window window = getWindow();
        window.setGravity(Gravity.LEFT|Gravity.TOP);
        WindowManager.LayoutParams params = window.getAttributes();
        params.x=0;
        params.y=0;
        params.height=1;
        params.width=1;
        window.setAttributes(params);
    }
}

其次,从AndroidManifest中通过如下属性,排除Activity在RecentTask中的显示。

<activity
    android:name=".KeepAliveActivity"
    android:excludeFromRecents="true"
    android:exported="false"
    android:finishOnTaskLaunch="false"
    android:launchMode="singleInstance"
    android:process=":live"
    android:theme="@style/LiveActivityStyle"
    >
</activity>

最后,控制Activity为透明。

<stylename="LiveActivityStyle">
   <itemname="android:windowBackground">@android:color/transparent</item>
   <itemname="android:windowFrame">@null</item>
   <itemname="android:windowNoTitle">true</item>
   <itemname="android:windowIsFloating">true</item>
   <itemname="android:windowIsTranslucent">true</item>
   <itemname="android:windowContentOverlay">@null</item>
   <itemname="android:windowAnimationStyle">@null</item>
   <itemname="android:windowDisablePreview">true</item>
   <itemname="android:windowNoDisplay">true</item>
</style>

Activity 启动与销毁时机的控制:

public class KeepLiveReceiver extendsBroadcastReceiver {
    privateContextmContext;

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(Intent.ACTION_SCREEN_OFF)) {
            KeepLiveManeger.getInstance(mContext).startKeepLiveActivity();
        } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
            KeepLiveManeger.getInstance(mContext).destroyKeepLiveActivity();
        }
        KeepLiveManeger.getInstance(mContext).startKeepLiveService();
    }
}


3.2 前台服务

利用前台服务保活也称灰色保活,这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行这一个后台Service进程一样。这样做带来的好处是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。原理如下所示:

  • API<18,启动前台Service时直接传入new Notification();
  • API>=18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理。

代码如下:

public class GrayService extends Service {

    private final static int GRAY_SERVICE_ID = 1001;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT < 18) {
            startForeground(GRAY_SERVICE_ID, new Notification());//API < 18 ,此方法能有效隐藏Notification上的图标
        } else {
            Intent innerIntent = new Intent(this, GrayInnerService.class);
            startService(innerIntent);
            startForeground(GRAY_SERVICE_ID, new Notification());
        }

        return super.onStartCommand(intent, flags, startId);
    }

    ...
    ...

    /**
     * 给 API >= 18 的平台上用的灰色保活手段
     */
    public static class GrayInnerService extends Service {

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            startForeground(GRAY_SERVICE_ID, new Notification());
            stopForeground(true);
            stopSelf();
            return super.onStartCommand(intent, flags, startId);
        }

    }
}

使用灰色保活并不代表着你的Service就永生不死了,只能说时提高了进程的优先级。如果你的app进程占用了大量的内存,按照回收进程的策略,同样会干掉你的app。

总结

本文讲述了根据进程优先级讲述了5中进程分别是前台进程,可见进程,服务进程,后台进程,空进程。然后讲述了进程回收策略。最后讲述了两种提高进程优先级的方法,1像素Activity和前台服务。下一篇文章进程保活(二)继续讲解进程死后拉活方案。

参考链接:
https://mp.weixin.qq.com/s?__biz=MzA3NTYzODYzMg==&mid=2653577617&idx=1&sn=623256a2ff94641036a6c9eea17baab8&scene=1&srcid=0818EecQYYkaSkd5HD8WjDf8#rd
https://blog.csdn.net/u013263323/article/details/56285475

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值