android应用被杀死
‘壹’ android app被杀死 alarmmanager能不能唤醒
可以唤醒的,但是得需要注意设置进程属性。
在Android中,AlarmManager提供了不受休眠状态的系统定时功能,其一般使用方法如下。
1、创建一个BroadcastReceiver类的子类,接收定时器事件:
public class MyReceiver extends BroadcastReceiver {
......
}
2、在AndroidMenifest.xml中定义上述广播事件接收类的定义:
<receiver android:name=".MyReceiver">
</receiver>
3、在程序中在需要时设置定时器:
Intent intent = new Intent(context,MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP , SystemClock.elapsedRealtime() + ms, pendingIntent);
经过ms毫秒之后,MyReceiver会被调用,从而实现定时触发。
‘贰’ android应用进程被杀死后,如何接收得到广播
在具有root权限的进程管理器清理后,便相当于系统中的强制停止了,广播接收器等自然都不能使用了。为了最大可能地防止被这类有root权限的进程管理器杀掉,可以在程序退出的时候杀掉自己的进程,这样自己的广播接收器还是可以使用的,但那些进程管理器就找不到我们的进程了,因为已经被我们自己杀死了。但是在广播接收器收到广播后,我们的应用里程就会再被创建,因此在处理完广播后要再次杀死我们自己的进程。
‘叁’ 三星成为杀死Android后台应用程序的非官方最严重罪犯
在过去的几年中,三星在将One UI皮肤构建为市场上最好的Android版本之一方面取得了长足的进步。好吧,看来事情正朝着错误的方向前进。显然,三星在Android 11中引入了一些激进的后台应用程序终止策略,这些策略以最差的方式排在榜首。
Don'tKillMyApp 是一个独立的网站,根据Android OEM处理后台应用程序管理的方式对它们进行排名。该网站研究了AOSP和Google自己的Pixels如何处理后台应用程序,并从那里查看了主要OEM如何改变该平台,从而导致用户看到其后台应用程序意外关闭。
多年以来,该榜单几乎保持不变,索尼和诺基亚等股票将近库存的Android制造商始终处于较低的排名(越低越好),而小米,华硕,Oppo等公司的更积极的改变则获得了更高的排名。华为长期以来一直位居榜首,一加紧随其后,三星落后。好吧,那已经改变了。
由于该公司在Android 11升级中所做的更改,因此在主动杀死后台应用程序方面,三星现在位居第一。三星已实施一项新限制,以防止应用程序在前台服务中保持唤醒锁定。此更改默认情况下处于启用状态,被描述为“与标准Android流程管理策略存在重大差异”,并且可能导致某些后台应用中断,包括某些 健康 应用可能会丢失收集传感器数据的功能。
幸运的是,有一种解决方法。用户可以进入几个不同的设置页面( “设置”>“应用程序”>“您的应用程序”>“电池”>“电池优化”>“所有应用程序”>“ [应用程序名称]”>“不优化” )并关闭电池优化。
值得注意的是,这是在Google表示将采取措施完全阻止其Android合作伙伴的这种行为之后。显然,三星没有得到该备忘录,也没有完全忽略它。
由于过去几个月一直在使用三星最新的Galaxy S21设备和Galaxy Z Fold 2的人,我不能说我直接注意到了这些问题。但是,这可能与以下事实直接相关:所有这些三星设备都拥有大量的RAM,从而导致后台应用程序无法使用。这个问题可能会使自己在三星价格较低的设备上更加清楚。
‘肆’ Android 应用进程被杀不能收到推送,这正常吗
非常正常!应用程序就是靠后台才能接受和发送推送消息,安卓手机都这样!只要关了后台,就会收不到消息!当然,像苹果或者安卓手机厂商一些高端旗舰机有推送服务,只保留一个系统程序就可以接收应用消息!
‘伍’ android应用被强杀或应用被回收导致的空指针问题等其他问题
在开发中我们经常会遇见app退到后台再打开会出现空指针、页面显示不全等一系列奇怪的问题。
当我们的进程被强杀或者被回收的时候,Android系统虽然让你的进程没有了,但是此进程中Activity中栈的信息还是存在的,也就是说此时当你点开此应用的时候程序中的Activity栈信息任然存在,只不过Activity中的数据都没有了,需要重新创建新的Activity数据。
分别涉及到:一个单例ConstantInstance 基类BaseAcyivity 首页MainActivity 启动页IndexActivity
‘陆’ Android APP在后台被杀问题修复
目前项目采用单 Activity 模式,页面采用 Jetpack Navigation 导航
布局如下:Splash -> Home -> Detail
不做任何事情
持有 LiveData 类型变量 hasSDKInit,根据SDK初始化成功与否设置 true 或者 false
调用 SDK 实现相关功能。
很明显,被杀后与 Activity 生命周期关联的 ViewModel 也结束了,与新打开 APP 的区别是,这时候是没有通过 Splash 去 初始化 SDK 的, Home 直接调用一个没有初始化的SDK 实例当然就报错了。
这样我们把 SDK 的初始化挪到 Activity::onCreate 就行了。
Fragment 中通过``ViewModelProvider(requireActivity()).get(GlobalViewModel::class.java)`获取。
把 init SDK 放到 ViewModel 的构造函数中,并将 initSDK 方法私有化,不允许从外部调用
context 通过新建 ViewModelFactory 类传入。
Google 官方不推荐 ViewModel 持有任何形式的 Context,如果确实要用,可以考虑单例或者 AndroidViewModel
Splash 页面监听 hasSDKInit 逻辑不变。
Home 页面原有逻辑不变,必要时加上对 hasSDKInit 的监听,true 才可进行后续操作。
而,如果SDK 的 init 不依赖网络等其他因素,默认情况下因为所有调用都在主线程,那么 就不用做监听。
‘柒’ 如何防止android app被kill
相较于/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/mole/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/mole/lowmemorykiller/parameters/minfree
要想重新设置该值(对应不同的需求):
echo "1536,2048,4096,5120,15360,23040">/sys/mole/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 mpsys 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文件中设置(如上)。
‘捌’ 为什么 12G 内存的 Android 手机依旧会被杀后台
不知不觉间 Android 陷入了一个关于“后台”的怪圈:一边各大厂商陆续推出了 12G RAM 的手机,另一边你刚刚放到后台的下载任务没有如预期那样后台挂机下载,打开微信发现还得陪启动画面的孤独小人共赏蓝色星球、按照教程辛辛苦苦做了半个小时的 Tasker 规则、却没有按照计划自动执行……
于是一个耳熟能详的句子开始在我们脑海中成型—— 我的后台又被“杀”了 。
应用开发者的“控诉”
遇到上述问题的人不止你一个,很多人选择向这些应用的开发者反馈问题,殊不知问题其实不在应用本身。
Android 平台着名睡眠追踪应用 Sleep as Android 的开发团队 Urbandroid Team 不堪其扰,索性上线了一个名为“别‘杀’我应用”的网站,矛头直指手机厂商糟糕的后台管理机制。
以三星为例,Urbandroid Team 称,三星的部分机型在升级到基于 Android 9 的 One UI 后“杀后台”现象变得尤为严重,自适应电池(Adaptive Battery)机制相比原生 Android 变得尤为激进,3 天内没有启动过的应用甚至无法从后台再次启动。最为糟糕的情况是,如果你安装了一个可以自动跳过周末的第三方闹钟,那这个闹钟应用很有可能不会像系统闹钟那样在下周一早上准时响起……
正如“别‘杀’我应用”网站上控诉的那样,拥有类似机制的还包括华为、一加、小米、华硕等等手机厂商的定制版 Android 系统,它们管理后台的方式大同小异,但都秉承着 iOS 上那一套“划掉就杀掉”的原则——当我们把某款应用的任务卡片从多任务界面划去,它们也就彻底从手机后台中抹除掉了。
这里你可能会问很多问题:
我们得从一些基础的原理说起。
Android 的内存回收机制
在 官方文档 中,Google 将“不受应用自身直接控制的应用进程生命周期”描述为 Android 最为基础也最为独特的核心特性,这里我们不妨将“应用进程生命周期”暂时理解为文章开头和第一部分所说的“后台”或“后台进程(process)”。
所以 Android 应用的后台进程去留本应是由 Android 系统来决定的 。
当可用运行内存空间不足时,Android 系统会自行决定对特定应用后台进程占用的空间进行回收释放,这个过程中 Android 挥舞着的那把大刀,叫做 LMK(Low Memory Killer)。那 LMK 又是如何判断哪些应用可以被“杀”掉、哪些应用又该暂时放过的呢?
每个应用都有各种各样的组成部分,其中特定组件的运行状态共同组成了一套供 LMK 进行内存回收的“优先级”参考,包括:前台进程、可见进程、服务进程和缓存进程。
前台进程、可见进程和服务进程往往与我们正在手机上执行的操作直接或间接相关 ,比如正在前台供我们交互和操作的活动窗口(Activity)、正在通过广播接收器(BroadcastReceiver)等待触发的 Tasker 规则、正在后台通过 Wi-Fi 网络自动上传备份照片的 Google Photos 以及前面提到的有待触发的闹钟等等。这些进程优先级从高到低依次递减,LMK 一般不会触及。
缓存进程则是那些暂时放在运行内存中的部分,也是和本文探讨话题主要相关的重点 。
只有在极端情况下 ,比如 Android 系统在回收掉所有缓存进程后发现空闲内存依然不够用(比如在低内存的“老爷机”上运行《崩坏 3》),这时 LMK 才会根据优先级继续对服务进程、可见进程和前台进程采取回收策略。 而当这些我们在正常使用中能够直观感受到的进程都不得不被被回收时 ,文章开头提到的微信重载、音乐中断、下载消失等等现象也就出现了。
谁动了你的后台
在可用内存充裕的情况下遭遇“杀后台”现象,一方面可能是 LMK 这把“大刀”出了问题(常见于 Android 9 时期的 Pixel 3 用户),另一方面则有可能是其它规则额外干预了 Android 系统正常的内存回收机制。
这里提到的“其它规则”主要有两种形式,一种类似部分华为设备上预装的“省电精灵”,它会将所有没有加入后台白名单中的应用后台统统清除,另一种则依托于 Google 推出的 后台检查 、 后台限制 和 自适应电池 等功能进行“魔改”,让这些功能的实际效果远超预期,甚至达到意料之外的负面效果。
根据 Don't kill my app! 的统计,第二种后台干预机制在三星、一加和早期的诺基亚机型中常见,这里厂商们通常会用到一种类似“白名单”的方法来进行过滤。
以三星手机基于 Android 9 的 One UI 为例,除了微信、QQ 等国内常见应用,One UI 默认会为所有第三方应用关闭“允许后台活动”这一选项,同时开启“优化电池使用量”这一功能。
部分搭载氢 OS 的一加机型则将上面提到的应用进程进行拆分,除了基于原生 Android 的后台限制、电池优化,还有一套名为“自启动管理”的设置来对应用的自启动进行管理以及一套名为“深度优化”的电池优化机制,后者会造成很多智能手表、手环设备在一段时间后丢失与手机的蓝牙连接,最终导致睡眠追踪、运动记录等等功能的失效。
问题在于上述功能埋藏较深,一般用户在安装应用后往往不会第一时间前往设置,一加的氢 OS 更是以系统更新之后自动重置部分用户设置闻名,那些需要在后台正常工作的应用,因此也被都被直接扔进了原生 Android 中用来限制“毒瘤”应用的“黑箱”里。
换句话说,国内大部分定制 ROM 在后台管理这件事情上都选择采用一种“ 宁肯错杀一千不肯放过一个 ”的做法。
关联阅读: 控制频繁启动的“毒瘤”,Android 9.0 用这些方法让你的手机更省电
多任务管理还是后台管理?
从某种程度上来说,国产手机厂商在 Android 后台管理上的做法虽然偏激,但它们都是国内特殊生态下的产物 。
一方面,尽管 Google 为 Android 设想了一套非常理想化的应用运行与后台管理机制 ,但大多数于原生 Android 中行之有效的后台管理机制在国内似乎都会变成“鸡肋”。
如果 Google 有 100 种提升 Android 应用运行效率,保证后台绿色、纯净的方法,国内毒瘤应用开发商就有 101 种绕过这些限制的方法。
借助共用的第三方推送服务实现链式唤醒、借助透明的悬浮窗保证后台存活、通过不断获取定位的方式来避免进程被系统回收……不管是出于实现消息推送这样单纯的目的还是为了不断唤醒用户设备以实现 KPI 目标这种下作的行为,在国内 Android 生态中均有出现。
虽然国内外的具体环境有所不同,但这类设计不规范的 Android 应用带来的问题却是一样的,这类应用放在后台不仅不会为我们带来便利,反而还会因为频繁唤醒设备带来不小的耗电问题。待机续航问题作为悬在国产 Android 机头顶的几把利剑之一,手机厂商不得不各自从系统层面推出自家的应对机制——这就有了上面提到的各种偏激式的后台管理方法。
另一方面,这里还涉及到一个非常重要的概念区分 :多任务管理和后台管理究竟是不是一回事?
国内 Android 生态由于早期受 iOS 影响较深,无论是开发商还是用户都更倾向于把“将应用卡片从多任务列表里划掉”的行为理解为清除对应用的后台进程。在上面提到的特殊生态环境的影响之下,这里被清除的后台进程往往又包括那些用于保证应用后台运行的可见进程、服务进程乃至前台进程在内。
在酷安应用市场,甚至还有得以在原生 Android 上实现类似“划掉卡片即停止运行”效果的应用,iOS 的后台管理理念在国内有多么深入人心可见一斑。
但这种后台管理理念却与 Google 对 Android 的多任务管理设计方式相悖。Google 一直以来都将 Android 手机上呼出任务卡片的那个界面叫做 Recents,最近几个版本的 Android 系统更是将其本地化为“概览”。结合 Google 在 Android 9 和 Android 10 手势交互上的变革,注重多任务管理而非后台管理的意图也越发明显。
当最近运行的应用以一张张卡片的形式呈现在我们面前时,Google 想要呈现的是一个能够让我们在不同任务间快速切换的多任务交互,而在理想状态下,后台管理则是交由系统处理、完全不应被用户感知的。
至于如何理性看待 Android 平台的后台管理,这里我们不妨借用绿色守护开发者 @OasisFeng 在“Android 多任务界面的划除交互”这个话题上的 答疑 来回答这个问题:
换句话说, 今后绝大部分需要在海外市场搭载 Google 服务上市的手机都必须满足这个要求 。
小结
就在上周三(9 月 25 日),酝酿已久的安卓统一推送联盟正式宣布收到华为、OPPO、一加和 realme 四家公司的进度确认,虽然 Google 的缺席也让国内 Android 生态也变得异常复杂,但国内 Android 设备也能用上的统一推送服务也算是终于迈出了具有实际意义的第一步。
只是距离转变人们对 Android“杀后台”这件事的看法依然还有很长的路要走。事实上,国内早在四五年前就出现过一次对“Android 需不需要‘杀后台’”问题的科普,但收效甚微,盲从 iOS 设计风格和交互逻辑国内 Android 厂商要负很大一部分责任。
希望靠谱、省电的统一推送系统能成为改观的第一步,也希望 @OasisFeng 口中那个甚至可以跨越设备重启恢复“后台状态”的理想化生态早日到来——至于当下,我们依然只能见招拆招,遇到应用无法正常执行后台任务时打开手机设置仔细翻找、设置,把它们扔进白名单或是给它们的后台卡片套个“锁”……
下载、安装了一款非白名单应用,该如何确保它不被“杀后台”呢?欢迎在评论区把你的设置方法分享给大家。