• Stars
    star
    135
  • Rank 269,297 (Top 6 %)
  • Language
    Java
  • Created over 5 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Android <=8.0 进程保活方案

如果文章中 gif 图片看不了,请移步博客

现在只要是社交 APP 没有哪个开发者不想让自己的 APP 永久常驻的,想要永久常驻除非你们家的实力非常雄厚,APP 用户量非常大,那么厂商都会主动来找你,把你们家的 APP 加入白名单。否则永久常驻是不可能甚至都不给你权限后台运行。既然不能永久常驻,那么我们有没有一个办法可以使我们的 APP 不那么容易被系统杀死勒?或者说是杀死后能主动唤醒,显然是可以的,下面我们进入主题吧。

怎么使用

代码传送阵

  1. down 代码 https://github.com/yangkun19921001/KeepAlive.git ,将 live_library 放入自己工程

  2. 在 KeepAliveRuning onRuning 中实现需要保活的代码

    public class KeepAliveRuning implements IKeepAliveRuning {
        /**这里实现 Socket / 推送 等一些保活组件*/
        @Override
        public void onRuning() {
            //TODO--------------------------------------------
            Log.e("runing?KeepAliveRuning", "true");
        }
    
        @Override
        public void onStop() {
            Log.e("runing?KeepAliveRuning", "false");
        }
    }
  3. 开启保活

        public void start() {
            //启动保活服务
            KeepAliveManager.toKeepAlive(
                    getApplication()
                    , HIGH_POWER_CONSUMPTION,
                    "进程保活",
                    "Process: System(哥们儿) 我不想被杀死",
                    R.mipmap.ic_launcher,
                    new ForegroundNotification(
                            //定义前台服务的通知点击事件
                            new ForegroundNotificationClickListener() {
                                @Override
                                public void foregroundNotificationClick(Context context, Intent intent) {
                                    Log.d("JOB-->", " foregroundNotificationClick");
                                }
                            })
            );
        }
  4. 停止保活

       KeepAliveManager.stopWork(getApplication());

最终效果

开启保活

我们应该知道正常的话点击手机回收垃圾桶后台的应用都会被 kill 掉,还有主动点击 AS Logcat 的进程停止运行的按钮,我们也会发现进程会自动起来并且 pid 跟上一次不一样了。要的就是这种效果,下面我们来了解下进程保活的知识吧.

未开启保活

进程优先级

官网详细介绍

进程

如果内存不足,需要为其他用户提供更紧急服务的进程又需要内存时,Android 可能会决定在某一时刻关闭某一进程。在被终止进程中运行的应用组件也会随之销毁。 当这些组件需要再次运行时,系统将为它们重启进程。

决定终止哪个进程时,Android 系统将权衡它们对用户的相对重要程度。例如,相对于托管可见 Activity 的进程而言,它更有可能关闭托管屏幕上不再可见的 Activity 的进程。 因此,是否终止某个进程的决定取决于该进程中所运行组件的状态。 下面,我们介绍决定终止进程所用的规则。

进程生命周期

Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要移除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然后是重要性略逊的进程,依此类推,以回收系统资源。

重要性层次结构一共有 5 级。以下列表按照重要程度列出了各类进程(第一个进程最重要,将是最后一个被终止的进程):

名称 概括 回收状态
前台进程 正在交互 只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们
可见进程 没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程 可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。
服务进程 正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。 除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。
后台进程 对用户不可见的 Activity 的进程 系统可能随时终止它们
空进程 不含任何活动应用组件的进程 最容易为杀死

LMK(LowMemoryKiller)

  • 为什么引入 LMK ?

    进程的启动分冷启动和热启动,当用户退出某一个进程的时候,并不会真正的将进程退出,而是将这个进程放到后台,以便下次启动的时候可以马上启动起来,这个过程名为热启动,这也是Android 的设计理念之一。这个机制会带来一个问题,每个进程都有自己独立的内存地址空间,随着应用打开数量的增多, 系统已使用的内存越来越大,就很有可能导致系统内存不足。为了解决这个问题,系统引入 LowmemoryKiller (简称 lmk ) 管理所有进程,根据一定策略来 kill 某个进程并释放占用的内存,保证系统的正常运行。

  • LMK 基本原理

    所有应用进程都是从 zygote 孵化出来的,记录在 AMS 中mLruProcesses 列表中,由 AMS 进行统一管理,AMS 中会根据进程的状态更新进程对应的 oom_adj 值,这个值会通过文件传递到 kernel 中去,kernel 有个低内存回收机制,在内存达到一定阀值时会触发清理 oom_adj 值高的进程腾出更多的内存空间

  • LMK 杀进程标准

    minfree : 存放6个数值,单位内存页面数 ( 一个页面 4kb )

    得到的数值为:18432 , 23040 , 27648 , 32256 , 36864 , 46080

    这 6 个数值分别代表 android 系统回收 6 种进程的阈值,这么看不方便查看,转换为 M 会更直观,这 6 个数值的单位为 page 1 page = 4 KB ,所以通过数值 * 4 / 1024 就能转换为M : 72 M , 90 M , 108 M , 126 M , 144 M , 180M

    也就是说:

    1.前台进程(foreground),2.可见进程(visible),3.次要服务(secondary server),4.后台进程(hidden),5.内容供应节点(content provider),6.空进程(empty)这6类进程进行回收的内存阈值分别为72M,90M,108M,126M,144M,180 M

    当内存到 180 M的时候会将空进程进行回收,当内存到 144 M 的时候把空进程回收完以后开始对内容供应节点进行回收,并不是所有的内容供应节点都回收,而是通过判断它的优先级进行回收,优先级是用 oom_adj 的值来表示,值越大回收的几率越高

    adj 查看:

    cat /sys/module/lowmemorykiller/parameters/adj

    查看进程 adj 值:

    adb shell ps

    值越低越不易被回收,0 代表就不会被回收。

内存阈值在不同的手机上不一样,一旦低于该值, Android 便开始按顺序关闭进程. 因此 Android 开始结束优先级最低的空进程,即当可用内存小于 180MB (46080)

进程保活方案

Activity 提权

这里可见 oom_adj 为 0 是不会被回收的

后台 oom_adj 为 6 内存不足会被回收

锁屏 oom_adj 开启一像素 Activity 为 0 相当于可见进程,不易被回收

实现原理:

监控手机锁屏解锁事件,在屏幕锁屏时启动 1 个像素透明的 Activity ,在用户解锁时将 Activity 销毁掉,从而达到提高进程优先级的作用。

代码实现

  1. 创建 onePxActivity

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //设定一像素的activity
            Window window = getWindow();
            window.setGravity(Gravity.START | Gravity.TOP);
            WindowManager.LayoutParams params = window.getAttributes();
            params.x = 0;
            params.y = 0;
            params.height = 1;
            params.width = 1;
            window.setAttributes(params);
            //在一像素activity里注册广播接受者    接受到广播结束掉一像素
            br = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    finish();
                }
            };
            registerReceiver(br, new IntentFilter("finish activity"));
            checkScreenOn("onCreate");
        }
  2. 创建锁屏开屏广播接收

        @Override
        public void onReceive(final Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {    //屏幕关闭的时候接受到广播
                appIsForeground = IsForeground(context);
                try {
                    Intent it = new Intent(context, OnePixelActivity.class);
                    it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    it.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                    context.startActivity(it);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //通知屏幕已关闭,开始播放无声音乐
                context.sendBroadcast(new Intent("_ACTION_SCREEN_OFF"));
            } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {   //屏幕打开的时候发送广播  结束一像素
                context.sendBroadcast(new Intent("finish activity"));
                if (!appIsForeground) {
                    appIsForeground = false;
                    try {
                        Intent home = new Intent(Intent.ACTION_MAIN);
                        home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        home.addCategory(Intent.CATEGORY_HOME);
                        context.getApplicationContext().startActivity(home);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                //通知屏幕已点亮,停止播放无声音乐
                context.sendBroadcast(new Intent("_ACTION_SCREEN_ON"));
            }
        }

Service 提权

创建一个前台服务用于提高 app 在按下 home 键之后的进程优先级

private void startService(Context context) {
        try {
            Log.i(TAG, "---》启动双进程保活服务");
            //启动本地服务
            Intent localIntent = new Intent(context, LocalService.class);
            //启动守护进程
            Intent guardIntent = new Intent(context, RemoteService.class);
            if (Build.VERSION.SDK_INT >= 26) {
                startForegroundService(localIntent);
                startForegroundService(guardIntent);
            } else {
                startService(localIntent);
                startService(guardIntent);
            }
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

注意如果开启 startForegroundService 前台服务,那么必须在 5 s内开启一个前台进程的服务通知栏,不会报 ANR

startForeground(KeepAliveConfig.FOREGROUD_NOTIFICATION_ID, notification);

广播拉活(在 8.0 以下很受用)

在发生特定系统事件时,系统会发出广播,通过在 AndroidManifest 中静态注册对应的广播监听器,即可在发生响应事件时拉活。但是从android 7.0 开始,对广播进行了限制,而且在 8.0 更加严格。

以静态广播的形式注册

<receiver android:name=".receive.NotificationClickReceiver">
<intent-filter>
<action android:name="CLICK_NOTIFICATION"></action>
</intent-filter>
</receiver>

全家桶 拉活

有多个 app 在用户设备上安装,只要开启其中一个就可以将其他的app 也拉活。比如手机里装了手 Q、QQ 空间、兴趣部落等等,那么打开任意一个 app 后,其他的 app 也都会被唤醒。

Service 机制拉活

将 Service 设置为 START_STICKY,利用系统机制在 Service 挂掉后自动拉活

只要 targetSdkVersion 不小于5,就默认是 START_STICKY。 但是某些 ROM 系统不会拉活。并且经过测试,Service 第一次被异常杀死后很快被重启,第二次会比第一次慢,第三次又会比前一次慢,一旦在短时间内 Service 被杀死 4-5 次,则系统不再拉起。

账号同步拉活(只做了解,不靠谱)

手机系统设置里会有 “帐户” 一项功能,任何第三方 APP 都可以通过此功能将数据在一定时间内同步到服务器中去。系统在将 APP 帐户同步时,会将未启动的 APP 进程拉活

JobScheduler 拉活(靠谱,8.0 官方推荐)

JobScheduler 允许在特定状态与特定时间间隔周期执行任务。可以利用它的这个特点完成保活的功能,效果即开启一个定时器,与普通定时器不同的是其调度由系统完成。

注意 setPeriodic 方法 在 7.0 以上如果设置小于 15 min 不起作用,可以使用setMinimumLatency 设置延时启动,并且轮询

    public static void startJob(Context context) {
        try {
            mJobScheduler = (JobScheduler) context.getSystemService(
                    Context.JOB_SCHEDULER_SERVICE);
            JobInfo.Builder builder = new JobInfo.Builder(10,
                    new ComponentName(context.getPackageName(),
                            JobHandlerService.class.getName())).setPersisted(true);
            /**
             * I was having this problem and after review some blogs and the official documentation,
             * I realised that JobScheduler is having difference behavior on Android N(24 and 25).
             * JobScheduler works with a minimum periodic of 15 mins.
             *
             */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                //7.0以上延迟1s执行
                builder.setMinimumLatency(KeepAliveConfig.JOB_TIME);
            } else {
                //每隔1s执行一次job
                builder.setPeriodic(KeepAliveConfig.JOB_TIME);
            }
            mJobScheduler.schedule(builder.build());

        } catch (Exception e) {
            Log.e("startJob->", e.getMessage());
        }
    }

推送拉活

根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送。

Native 拉活

Native fork 子进程用于观察当前 app 主进程的存亡状态。对于 5.0以上成功率极低。

后台循环播放一条无声文件

//如果选择流氓模式,就默认接收了耗电的缺点,但是保活效果很好。
if (mediaPlayer == null && KeepAliveConfig.runMode == RunMode.HIGH_POWER_CONSUMPTION) {
            mediaPlayer = MediaPlayer.create(this, R.raw.novioce);
            mediaPlayer.setVolume(0f, 0f);
            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mediaPlayer) {
                    Log.i(TAG, "循环播放音乐");
                    play();
                }
            });
            play();
        }

双进程守护 (靠谱)

两个进程相互绑定 (bindService),如果有其中一个进程被杀,那么另外一个进程就会将被杀的进程重新拉起

总结

进程保活就讲到这里了,最后我自己是结合里面最靠谱的(Activity + Service 提权 + Service 机制拉活 + JobScheduler 定时检测进程是否运行 + 后台播放无声文件 + 双进程守护),然后组成了一个进程保活终极方案。 文章中只是部分代码,感兴趣的可以下载 demo 试下保活效果。

More Repositories

1

Blog

Android 面试宝典、数据结构和算法、音视频 (FFmpeg、AAC、x264、MediaCodec)、 C/C++ 、OpenCV、跨平台等学习记录。【0基础音视频进阶学习路线】
HTML
2,030
star
2

AVSample

0 基础音视频进阶路线 (MediaCodec、FFmpeg、OpenCV、OpenGL、短视频 SDK、音视频播放器、webrtc)
C
428
star
3

AVEditor

这是一款短视频编辑 SDK,仿 DouYin 音视频处理。功能包含有美颜、滤镜、贴纸、特效、录制、分段录制、速率录制、变声、配乐、rtmp 直播推流、图片转视频、剪辑,mp4/flv 格式封装等功能。动态库用的我另一个项目编译好的 https://github.com/yangkun19921001/AVFFmpegLib
C
362
star
4

DexEncryptionDecryption

APK 加固 dex 加密,解密 学习项目
Java
223
star
5

NDK_AV_SAMPLE

Android NDK 音视频 FFmpeg OpenGL OpenCV rtmp 直播 学习记录! 想要更全面的学习音视频可以点击👉(适合零基础入门音视频专栏)
C
151
star
6

AVFFmpegLib

移植 FFmpeg 最新版本v4.4-dev-416 + libx264 + freetype + fontconfig + fribidi + openh264 +libfdk-aac + gnutls + speex + libwebp + lame +opus + opencore-amr + https)编译的适用于 Android 平台的音视频编辑、视频剪辑的快速处理框架,目前内置了音视频剪辑、编辑、视频拼接、字幕、水印、倒放等功能,也可以根据 ffmpeg 命令模式来进行处理。
C
111
star
7

AVRtmpPushSDK

这是一个由 kotlin 、C++ 编写的 RTMP 推流项目
Kotlin
109
star
8

YKCrash

Android 崩溃捕获(Java,native)
C++
47
star
9

YKAVStudyPlatform

ffmpeg、ffplay、ffprobe 调试环境搭建,个人音视频全平台学习记录
C
38
star
10

AudioManager

这是一个 Android 端万能音频播放器,只要给我一个路径我就能进行播放。
C
37
star
11

LIBJPEG_SAMPLE

Android libjpeg 图片压缩,哈夫曼算法图片压缩
C
36
star
12

OpenRTCProject

基于 webrtc 实现的各种项目
JavaScript
36
star
13

long_picture_view

加载 长图控件,仿微博加载方式
Java
28
star
14

CustomViewSample

自定义View 练习 demo
Kotlin
23
star
15

Kotlin_GitHub

基于 Kotlin + MVP + retrofit + Okhttp +RxJava + Glide + anko + sw 限定符做了屏幕适配 等框架编写的 GitHub APP ,仅供学习
Kotlin
23
star
16

YKProBus

轻量级进程间通信框架,基于 Messenger 实现。进程间传递数据不错的选择
Java
23
star
17

MemoryFileWriteBytesYUV

解决执法仪本地录制和第三方软件同时使用摄像头资源不被占用的问题
Java
21
star
18

QTFFPlayer

MAC、Win、Linux、Android、IOS 跨平台播放器开发
C++
16
star
19

ffplay2

基于 ffplay 实现的 PC/Android/IOS 跨平台播放器
C
10
star
20

YKComponent

Android 组件化框架探索
Java
7
star
21

AndroidDpCode

Android 中 设计模式探索
Java
6
star
22

hotfix

基于类加载的热修复
Java
6
star
23

FWX_Component_Project

高仿微信(学习项目)
Java
6
star
24

ChatUI_Component

聊天输入框,列表,录像等框架
Java
6
star
25

YUVPlay

OpenGL, YUV 渲染
Java
6
star
26

T01_AV_SDKDEMO

[凌壹世纪科技有限公司](http://www.t01.com.cn/) VOIP 融合通信 SDK
Java
5
star
27

WebRTCSample

从 WebRTC 中抽代码
Java
5
star
28

WebFFplay

web 端基于 ffmpeg 实现音视频播放器的一个 demo
JavaScript
5
star
29

media_codec_sample

native 层 MediaCodec 硬编码代码提供 mk / cmake 编译 .s /.so
C
4
star
30

native_mediacodec_test

测试 native 调用 MediaCodec 代码
C++
4
star
31

FaceDetectionOpenCV

OpenCV+Dlib 人脸识别项目
C++
3
star
32

YEventBus

一个轻量级 EventBus ,支持根据 TAG 接收事件
Java
3
star
33

YKPluginAPK

Android 中插件化探索,动态加载 APK 文件中(Activity,Servicer,Broadcast)等一些资源。
Java
3
star
34

BaseComponent

基于前辈大神修改为属于自己的组件化框架 https://github.com/JessYanCoding/ArmsComponent/wiki
Java
3
star
35

ndk_sample

Android 调用 C/C++ 最简单的一个实例 零基础易看懂
Java
3
star
36

CustomHandler

实现简易的 Handler 线程间通信架构
Java
3
star
37

AndroidComponentTest

Android中一些常用的 Utils 或者 组件学习代码
Java
2
star
38

NDK_OpenGL_ES3.0

OpenGL ES 3.0 学习记录
C
2
star
39

YKDB

轻量级数据库,不用写 SQL ,仅仅通过注解就能完成对数据库的操作
Java
2
star
40

TTScreenDensity-master

今日头条适配方案
Java
2
star
41

solo-blog

DevYK 的个人博客 - 记录精彩的程序人生
1
star
42

h264-extractor

Wireshark 中获取 H264 视频码流
Lua
1
star
43

KingKongRTC

This is a cross-platform RTC library
C++
1
star
44

battery

电量优化 部分代码
Java
1
star
45

component_project

基于前辈大神修改为属于自己的组件化框架 https://github.com/JessYanCoding/ArmsComponent/wiki
Java
1
star
46

BitmapMemoryManager

Bitmap 三级缓存
Java
1
star