作者:Yocn
首先一个报错来作为开篇:
Caused by androidx.fragment.app.Fragment$InstantiationException
Unable to instantiate fragment xxx: could not find Fragment constructor
这个报错原因就是Fragment如果重载了有参的构造方法,没有实现默认无参构造方法。Activity被回收又回来尝试重新恢复Fragment的时候报错的。
那如何模拟Activity被回收呢?
可能有人知道,一个方便快捷的方法就是:打开 开发者选项
- 不保留活动
,这样每次Activity回到后台都会被回收,也就可以很方便的测试这种case。
但抛开这种方式我怎么来复现这种情况呢?
这里我提出一种方式:我是不是可以打开我的App,按Home回到后台,然后疯狂的打开手机里其他的大型应用或者游戏这类的能占用大量手机内存的App,等手机内存占用大的时候是不是可以复现这种情况呢?
结论是不可以,不要混淆两个概念,系统内存不足
和App内存不足
,两者能引起的后果也是不同的
- 系统内存不足 -> 杀掉应用进程
- App内存不足 -> 杀掉后台Activity
首先明确一点,Android框架对进程创建与管理进行了封装,对于APP开发者只需知道Android四大组件的使用。当Activity, Service, ContentProvider, BroadcastReceiver任一组件启动时,当其所承载的进程存在则直接使用,不存在则由框架代码自动调用startProcessLocked创建进程。所以说对APP来说进程几乎是透明的,但了解进程对于深刻理解Android系统是至关关键的。
1. 系统内存不够 -> 杀掉应用进程
1.1. LKM简介
Android底层还是基于Linux,在Linux中低内存是会有oom killer去杀掉一些进程去释放内存,而Android中的lowmemorykiller
就是在此基础上做了一些调整来的。因为手机上的内存毕竟比较有限,而Android中APP在不使用之后并不是马上被杀掉,虽然上层ActivityManagerService
中也有很多关于进程的调度以及杀进程的手段,但是毕竟还需要考虑手机剩余内存的实际情况,lowmemorykiller的作用就是当内存比较紧张的时候去及时杀掉一些ActivityManagerService
还没来得及杀掉但是对用户来说不那么重要的进程,回收一些内存,保证手机的正常运行。
lowmemkiller中会涉及到几个重要的概念:
/sys/module/lowmemorykiller/parameters/minfree:里面是以”,”分割的一组数,每个数字代表一个内存级别
/sys/module/lowmemorykiller/parameters/adj: 对应上面的一组数,每个数组代表一个进程优先级级别
比如:
/sys/module/lowmemorykiller/parameters/minfree:18432, 23040, 27648, 32256, 55296, 80640
/sys/module/lowmemorykiller/parameters/adj: 0, 100, 200, 300, 900, 906
代表的意思是两组数一一对应:
- 当手机内存低于
80640
时,就去杀掉优先级906
以及以上级别的进程 - 当内存低于
55296
时,就去杀掉优先级900
以及以上的进程
可能每个手机的配置是不一样的,可以查看一下手头的手机,需要root。
1.2. 如何查看ADJ
如何查看进程的ADJ
呢?比如我们想看QQ的adj
-> adb shell ps | grep "qq"
UID PID PPID C STIME TTY TIME CMD
u0_a140 9456 959 2 10:03:07 ? 00:00:22 com.tencent.mobileqq
u0_a140 9987 959 1 10:03:13 ? 00:00:07 com.tencent.mobileqq:mini3
u0_a140 16347 959 0 01:32:48 ? 00:01:12 com.tencent.mobileqq:MSF
u0_a140 21475 959 0 19:47:33 ? 00:01:25 com.tencent.mobileqq:qzone
# 看到QQ的PID为 9456,这个时候打开QQ,让QQ来到前台
-> adb shell cat /proc/9456/oom_score_adj
0
# 随便打开一个其他的App
-> adb shell cat /proc/9456/oom_score_adj
700
# 再随便打开另外一个其他的App
-> adb shell cat /proc/9456/oom_score_adj
900
我们可以看到adj是在根据用户的行为不断变化的,前台的时候是0
,到后台是700
,回到后台后再打开其他App后是900
。
常见ADJ级别如下:
ADJ级别 | 取值 | 含义 |
---|---|---|
NATIVE_ADJ | -1000 | native进程 |
SYSTEM_ADJ | -900 | 仅指system_server进程 |
PERSISTENT_PROC_ADJ | -800 | 系统persistent进程 |
PERSISTENT_SERVICE_ADJ | -700 | 关联着系统或persistent进程 |
FOREGROUND_APP_ADJ | 0 | 前台进程 |
VISIBLE_APP_ADJ | 100 | 可见进程 |
PERCEPTIBLE_APP_ADJ | 200 | 可感知进程,比如后台音乐播放 |
BACKUP_APP_ADJ | 300 | 备份进程 |
HEAVY_WEIGHT_APP_ADJ | 400 | 重量级进程 |
SERVICE_ADJ |