版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
一、进程的优先级
Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,需要清除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会对进程进行分类。 需要时,系统会首先消除重要性最低的进程,然后是清除重要性稍低一级的进程,依此类推,以回收系统资源。
进程的重要性层次结构一共有 6 级:(官方文档只写 5 级)
1.前台进程(foreground)
用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:
- 托管用户正在交互的 Activity(已调用 Activity 的 onResume() 方法)
- 托管某个 Service,后者绑定到用户正在交互的 Activity
- 托管正在“前台”运行的 Service(服务已调用 startForeground())
- 托管正执行一个生命周期回调的 Service(onCreate()、onStart() 或 onDestroy())
- 托管正执行其 onReceive() 方法的 BroadcastReceiver
通常,在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。 此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。
2.可见进程(visible)
没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:
- 托管不在前台、但仍对用户可见的 Activity(已调用其 onPause() 方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况。
- 托管绑定到可见(或前台)Activity 的 Service。
可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。
3.服务进程(secondary server)
正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。
4.后台进程(hidden)
包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop() 方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。 有关保存和恢复状态的信息,请参阅 Activity文档。
5.内容供应节点(content provider)
没有程序实体,进提供内容供别的程序去用的,比如日历供应节点,邮件供应节点等。在终止进程时,这类程序应该有较高的优先权。
6.空进程(empty)
不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。
具体可以查看安卓官方中文网相关内容。
二、进程释放
1.Low Memory Killer
系统出于体验和性能上的考虑,app 在退到后台时系统并不会真正的 kill 掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。
在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要 kill 掉哪些进程,以腾出内存来供给需要的 app, 这套杀进程回收内存的机制就叫 Low Memory Killer。
查看阈值指令:
cat /sys/module/lowmemorykiller/parameters/minfree
这个结果是6个数字,以逗号隔开,如:
这 6 个值分别对应上面 6 级进程的的阈值。单位是 page, 1 page 为 4 个 KB,如最后一个为 46080 * 4 = 184320 KB,为 180 MB。当内存少于 180 MB 的时候,会进行回收空进程所占用的内存。
内存阈值在不同的手机上不一样,一旦低于该值,Android 便开始按顺序关闭进程。
2.oom_adj
进程的优先级通过进程的 adj 值来反映,它是 linux 内核分配给每个系统进程的一个值,进程回收机制根据这个值来决定是否进行回收。adj 的值越小,进程的优先级越高。
查看当前进程的 adj 值(需要 root 权限):
cat /proc/进程 id/oom_adj
应用正在运行:
按 Home 键隐藏后(不同的ROM可能不一样):
进程 id 可以直接通过开发工具查看:
adj 值的解释:
adj 越大,占用内存越多会被先被 kill 掉,所以保活就成了降低 oom_adj 的值,以及如何使得我们应用占的内存最少。
三、提权
由上面分析可以知道,在内存不足的时候,安卓系统会根据应用程序的 adj 来判断释放程序的先后顺序,为了使我们的应用更久的运行在安卓系统中,我们可以通过一些方法使应用的 adj 尽可能的变小,这样就可以尽可能的晚一点被释放,甚至不被释放。
1.Activity 提权
监控手机锁屏解锁事件,在屏幕锁屏时启动 1 个像素透明的 Activity,在用户解锁时将 Activity 销毁掉。从而达到在锁屏时候提高进程优先级的作用。
我们新建一个大小只有 1 个像素的 Activity。
KeepActivity:
public class KeepActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = getWindow();
//设置这个 Activity 在左上角
window.setGravity(Gravity.START | Gravity.TOP);
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.width = 1;
attributes.height = 1;
attributes.x = 0;
attributes.y = 0;
window.setAttributes(attributes);
//保存当前 activity,释放的时候需要使用
KeepManager.getInstance().setKeep(this);
}
}
在 AndroidManifest.xml 中配置这个 activity 在最近列表不显示,以及新建堆栈堆栈保存该 Activity。设置样式窗口背景为空并且透明。
AndroidManifest.xml
<!-- android:excludeFromRecents 设置在最近列表中不显示 -->
<!-- android:taskAffinity 设置新建堆栈保存该 Activity,
不指定新的堆栈,在息屏亮屏后,改应用会自动显示出来 -->
<activity android:name=".activity.KeepActivity"
android:excludeFromRecents="true"
android:taskAffinity="com.xiaoyue.myapplication.activity.KeepActivity"
android:theme="@style/KeepTheme">
</activity>
styles.xml:
<!-- 提权 Activity Style -->
<style name="KeepTheme">
<!-- 添加窗口背景为空 -->
<item name="android:windowBackground">@null</item>
<!-- 添加窗口为透明的 -->
<item name="android:windowIsTranslucent">true</item>
</style>
新建 KeepActivity 的管理类 KeepManager,管理 Activity 提权时的广播注册与反注册,Activity 的启动与释放。
KeepManager:
public class KeepManager {
private WeakReference<Activity> mKeepAct;
private KeepReceiver mKeepReceiver;
private static final KeepManager ourInstance = new KeepManager();
public static KeepManager getInstance() {
return ourInstance;
}
private KeepManager() {
}
/**
* 注册息屏亮屏广播
* @param context
*/
public void registerKeep(Context context) {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
mKeepReceiver = new KeepReceiver();
context.registerReceiver(mKeepReceiver, filter);
}
/**
* 反注册广播接收者
* @param context
*/
public void unregisterKeep(Context context) {
if (null != mKeepReceiver) {
context.unregisterReceiver(mKeepReceiver);
}
}
/**
* 开启 Activity
* @param context
*/
public void startKeep(Context context) {
Intent intent = new Intent(context, KeepActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
/**
* 结束 Activity
*/
public void finishKeep() {
if (null != mKeepAct) {
Activity activity = mKeepAct.get();
if (null !&