> 技术文档 > Android 14进程冻结流程总结并解决相关问题_android进程冻结

Android 14进程冻结流程总结并解决相关问题_android进程冻结


1. 进程冻结初始化开启流程

1.1 加载SystemServer的run方法

frameworks/base/services/java/com/android/server/SystemServer.java

public static void main(String[] args) { new SystemServer().run(); }
private void run() { ... // Setup the default WTF handler RuntimeInit.setDefaultApplicationWtfHandler(SystemServer::handleEarlySystemWtf); // Start services. try { t.traceBegin(\"StartServices\"); startBootstrapServices(t); startCoreServices(t); startOtherServices(t);//这里开始加载冻结相关服务 startApexServices(t); // Only update the timeout after starting all the services so that we use // the default timeout to start system server. updateWatchdogTimeout(t); } catch (Throwable ex) { Slog.e(\"System\", \"******************************************\"); Slog.e(\"System\", \"************ Failure starting system services\", ex); throw ex; } finally { t.traceEnd(); // StartServices } ... }

在run方法里面会加载几个服务,这里我们只关注startOtherServices(t)

 private void startOtherServices(@NonNull TimingsTraceAndSlog t) { ...  t.traceBegin(\"InstallSystemProviders\");  mActivityManagerService.getContentProviderHelper().installSystemProviders();  // Device configuration used to be part of System providers  mSystemServiceManager.startService(UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS);  // Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlags  SQLiteCompatibilityWalFlags.reset();  t.traceEnd(); ... }

在startOtherServices(t)方法里面,ams通过获取内容提供者帮助类调用installSystemProviders(),这个方法就是初始化冻结机制的开始,先来看看这个方法如何初始化冻结机制的。

1.2 ContentProviderHelper.java

frameworks/base/services/core/java/com/android/server/am/ContentProviderHelper.java

public final void installSystemProviders() { ... mService.mConstants.start(mService.mContext.getContentResolver()); mService.mCoreSettingsObserver = new CoreSettingsObserver(mService); mService.mActivityTaskManager.installSystemProviders(); new DevelopmentSettingsObserver(); // init to observe developer settings enable/disable SettingsToPropertiesMapper.start(mService.mContext.getContentResolver()); mService.mOomAdjuster.initSettings();//开始初始化冻结机制 // Now that the settings provider is published we can consider sending in a rescue party. RescueParty.onSettingsProviderPublished(mService.mContext); }

这里通过AMS服务调用OomAdjuster对象来初始化冻结机制,继续进入到initSettings()方法内部

1.3 OomAdjuster.java

frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java

 void initSettings() { mCachedAppOptimizer.init(); mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor()); ... }

这里面又进入到了另一个类中开始初始化操作,重点关注CachedAppOptimizer类中的init干了什么

1.4 CachedAppOptimizer.java

frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java

public void init() { // TODO: initialize flags to default and only update them if values are set in DeviceConfig DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener); DeviceConfig.addOnPropertiesChangedListener( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, ActivityThread.currentApplication().getMainExecutor(), mOnNativeBootFlagsChangedListener); mAm.mContext.getContentResolver().registerContentObserver(//注册观察者,监视冻结功能在数据库中的变化 CACHED_APP_FREEZER_ENABLED_URI, false, mSettingsObserver); synchronized (mPhenotypeFlagLock) { updateUseCompaction(); updateCompactionThrottles(); updateCompactStatsdSampleRate(); updateFreezerStatsdSampleRate(); updateFullRssThrottle(); updateFullDeltaRssThrottle(); updateProcStateThrottle(); updateUseFreezer();//开始初始化冻结功能 updateMinOomAdjThrottle(); updateMaxOomAdjThrottle(); } }

在初始化时给冻结功能的使能添加了数据库监听,接着进入到updateUseFreezer()方法中,这个方法里面首先会调用isFreezerSupported()方法判断系统是否支持冻结功能,方法内部会打开冻结相关文件,调用驱动来判定系统是否支持冻结,在此不进行追了,了解下即可。

@GuardedBy(\"mPhenotypeFlagLock\") private void updateUseFreezer() { final String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(), Settings.Global.CACHED_APPS_FREEZER_ENABLED);//获取数据库中冻结功能是否开启信息,根据这个值决定是否开启冻结功能 if (\"disabled\".equals(configOverride)) { mUseFreezer = false; } else if (\"enabled\".equals(configOverride) || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,  KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) { mUseFreezer = isFreezerSupported();//这里判断系统是否支持开启冻结,里面涉及到相关驱动是否加载 updateFreezerDebounceTimeout(); updateFreezerExemptInstPkg(); } else { mUseFreezer = false; } final boolean useFreezer = mUseFreezer; // enableFreezer() would need the global ActivityManagerService lock, post it. mAm.mHandler.post(() -> { if (useFreezer) { Slog.d(TAG_AM, \"Freezer enabled\"); enableFreezer(true);//开启冻结功能 if (!mCachedAppOptimizerThread.isAlive()) {  mCachedAppOptimizerThread.start(); } if (mFreezeHandler == null) {  mFreezeHandler = new FreezeHandler(); } Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), Process.THREAD_GROUP_SYSTEM); } else { Slog.d(TAG_AM, \"Freezer disabled\"); enableFreezer(false); } }); }

        最后会调用enableFreezer(boolean)方法开启冻结,我们进入到该方法内部看看:

public synchronized boolean enableFreezer(boolean enable) { ...  final ProcessCachedOptimizerRecord opt = process.mOptRecord;  if (enable && opt.hasFreezerOverride()) {//这个判断条件中opt.hasFreezerOverride()这个首次应该是默认为false,需要进行set freezeAppAsyncLSP(process);//这里往后就开始冻结进入进程了 opt.setFreezerOverride(false);  } ... }

这个方法中判断条件opt.hasFreezerOverride()首次应该为false,而下面的freezeAppAsyncLSP(process);就是开始冻结进程的方法,既然上面的条件没有走进去,那接下来就看看什么情况下会调用freezeAppAsyncLSP(process)将进程冻结,在此之前先总结下上述流程,如下图:

2. 进程冻结触发流程

2.1 冻结触发的原因

当进程的优先级(adj)发生变化的时候,会去计算判断,并执行冻结,以下是进程的优先级(adj)发生变化的原因:

public class OomAdjuster { static final String TAG = \"OomAdjuster\"; public static final int oomAdjReasonToProto(@OomAdjReason int oomReason) { switch (oomReason) { case OOM_ADJ_REASON_NONE: return AppProtoEnums.OOM_ADJ_REASON_NONE; case OOM_ADJ_REASON_ACTIVITY: return AppProtoEnums.OOM_ADJ_REASON_ACTIVITY; case OOM_ADJ_REASON_FINISH_RECEIVER: return AppProtoEnums.OOM_ADJ_REASON_FINISH_RECEIVER; case OOM_ADJ_REASON_START_RECEIVER: return AppProtoEnums.OOM_ADJ_REASON_START_RECEIVER; case OOM_ADJ_REASON_BIND_SERVICE: return AppProtoEnums.OOM_ADJ_REASON_BIND_SERVICE; case OOM_ADJ_REASON_UNBIND_SERVICE: return AppProtoEnums.OOM_ADJ_REASON_UNBIND_SERVICE; case OOM_ADJ_REASON_START_SERVICE: return AppProtoEnums.OOM_ADJ_REASON_START_SERVICE; case OOM_ADJ_REASON_GET_PROVIDER: return AppProtoEnums.OOM_ADJ_REASON_GET_PROVIDER; case OOM_ADJ_REASON_REMOVE_PROVIDER: return AppProtoEnums.OOM_ADJ_REASON_REMOVE_PROVIDER; case OOM_ADJ_REASON_UI_VISIBILITY: return AppProtoEnums.OOM_ADJ_REASON_UI_VISIBILITY; case OOM_ADJ_REASON_ALLOWLIST: return AppProtoEnums.OOM_ADJ_REASON_ALLOWLIST; case OOM_ADJ_REASON_PROCESS_BEGIN: return AppProtoEnums.OOM_ADJ_REASON_PROCESS_BEGIN; case OOM_ADJ_REASON_PROCESS_END: return AppProtoEnums.OOM_ADJ_REASON_PROCESS_END; case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT: return AppProtoEnums.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT; case OOM_ADJ_REASON_SYSTEM_INIT: return AppProtoEnums.OOM_ADJ_REASON_SYSTEM_INIT; case OOM_ADJ_REASON_BACKUP: return AppProtoEnums.OOM_ADJ_REASON_BACKUP; case OOM_ADJ_REASON_SHELL: return AppProtoEnums.OOM_ADJ_REASON_SHELL; case OOM_ADJ_REASON_REMOVE_TASK: return AppProtoEnums.OOM_ADJ_REASON_REMOVE_TASK; case OOM_ADJ_REASON_UID_IDLE: return AppProtoEnums.OOM_ADJ_REASON_UID_IDLE; case OOM_ADJ_REASON_STOP_SERVICE: return AppProtoEnums.OOM_ADJ_REASON_STOP_SERVICE; case OOM_ADJ_REASON_EXECUTING_SERVICE: return AppProtoEnums.OOM_ADJ_REASON_EXECUTING_SERVICE; case OOM_ADJ_REASON_RESTRICTION_CHANGE: return AppProtoEnums.OOM_ADJ_REASON_RESTRICTION_CHANGE; case OOM_ADJ_REASON_COMPONENT_DISABLED: return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED; default: return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO; } } ...}

从上我们可以看到当进程状态或者组件状态发生变化的时候,就会触发adj的更新,这里以Activity的状态变化为例。

2.2 ActivityRecord.setState

可以看到Activity的STARTED状态和DESTROYING状态调用了updateProcessInfo方法去更新进程状态,updateOomAdj都是true

frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

 void setState(State state, String reason) { ... case STARTED:  // Update process info while making an activity from invisible to visible, to make  // sure the process state is updated to foreground.  if (app != null) {app.updateProcessInfo(false /* updateServiceConnectionActivities */, true /* activityChange */, true /* updateOomAdj */, true /* addPendingTopUid */);  } ...  case :  if (app != null && !app.hasActivities()) {// Update any services we are bound to that might care about whether// their client may have activities.// No longer have activities, so update LRU list and oom adj.app.updateProcessInfo(true /* updateServiceConnectionActivities */, false /* activityChange */, true /* updateOomAdj */, false /* addPendingTopUid */);  } ...  }
tips其他状态,如 RESUMED、PAUSED、STOPPED 等,已经隐含或明确地处理了进程的优先级和状态管理,因此不需要在这些状态中调用 updateProcessInfo。RESUMED 状态:在 RESUMED 状态中,Activity 已经完全进入前台,并且通常在进入此状态时,进程已经被提升为前台优先级,因此不需要再次调用 updateProcessInfo。PAUSED 和 STOPPED 状态:这些状态下,Activity 逐渐变得不可见或不再与用户交互。通常系统会依赖其他机制来管理进程的优先级变化,而不需要在这两个状态中特别调用 updateProcessInfo。
2.3 ProcessRecord.updateProcessInfo

生命周期变化后,会调用到updateOomAdjLocked来继续更新adj的流程

frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java

public void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange,  boolean updateOomAdj) {  ...  mService.updateLruProcessLocked(this, activityChange, null /* client */);  if (updateOomAdj) {  mService.updateOomAdjLocked(this, OOM_ADJ_REASON_ACTIVITY);  }  ... }
2.4 ActivityManagerService.updateOomAdjLocked
 @GuardedBy(\"this\") //这是一个线程安全注解,表示这个方法在调用时,必须在当前对象实例的锁,在这也就是am的锁 final void updateOomAdjLocked(@OomAdjReason int oomAdjReason) { mOomAdjuster.updateOomAdjLocked(oomAdjReason); }
2.5 OomAdjuster
 * Update OomAdj for all processes in LRU list */ @GuardedBy(\"mService\") void updateOomAdjLocked(String oomAdjReason) { synchronized (mProcLock) { updateOomAdjLSP(oomAdjReason); } } @GuardedBy({\"mService\", \"mProcLock\"}) private void updateOomAdjLSP(String oomAdjReason) { ... mOomAdjUpdateOngoing = true; performUpdateOomAdjLSP(oomAdjReason); ... }@GuardedBy({\"mService\", \"mProcLock\"}) private void performUpdateOomAdjLSP(String oomAdjReason) { ... updateOomAdjInnerLSP(oomAdjReason, topApp , null, null, true, true); ... }@GuardedBy({\"mService\", \"mProcLock\"}) private void updateOomAdjInnerLSP(String oomAdjReason, final ProcessRecord topApp, ArrayList processes, ActiveUids uids, boolean potentialCycles, boolean startProfiling) { ... computeOomAdjLSP(app, ProcessList.UNKNOWN_ADJ, topApp, fullUpdate, now, false, computeClients); //计算进程的adj  boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids);  ... } @GuardedBy({\"mService\", \"mProcLock\"}) private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed, final long oldTime, final ActiveUids activeUids) { ...applyOomAdjLSP(app, true, now, nowElapsed); ... } @GuardedBy({\"mService\", \"mProcLock\"}) private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, long nowElapsed) { ... updateAppFreezeStateLSP(app); ... } 
2.6 OomAdjuster.updateAppFreezeStateLSP

当adj发生变化,然后计算,最终会去调用updateAppFreezeStateLSP更新app的冻结状态,在里面会去判断是否要用冻结机制,当前应用是否免疫冻结,当前是否已经被冻结等状态,然后和 ProcessList.CACHED_APP_MIN_ADJ(900)进行比较,当前当前adj大于等于它的时候,才回去调用冻结方法。

@GuardedBy({\"mService\", \"mProcLock\"}) private void updateAppFreezeStateLSP(ProcessRecord app) { if (!mCachedAppOptimizer.useFreezer()) { // 是否使用冻结机制 return; } if (app.mOptRecord.isFreezeExempt()) { // 是否免冻结, 这里追溯过去,目前只有判断拥有INSTALL_PACKAGES权限的进程能够被豁免 return; } final ProcessCachedOptimizerRecord opt = app.mOptRecord; // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze if (opt.isFrozen() && opt.shouldNotFreeze()) { //如果已经被冻结,并且不应该被冻结 mCachedAppOptimizer.unfreezeAppLSP(app); //解除冻结 return; } final ProcessStateRecord state = app.mState; // Use current adjustment when freezing, set adjustment when unfreezing.  if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ && !opt.isFrozen() //最小冻结adj 为public static final int CACHED_APP_MIN_ADJ = 900; && !opt.shouldNotFreeze()) { //当前 adj 大于最小冻结 adj 并且没有被冻结并且应该被冻结  mCachedAppOptimizer.freezeAppAsyncLSP(app); } else if (state.getSetAdj() < ProcessList.CACHED_APP_MIN_ADJ) { mCachedAppOptimizer.unfreezeAppLSP(app); //当前 adj 小于最小冻结 adj 应该解冻 } }}
2.7 CachedAppOptimizer.freezeAppAsyncLSP

freezeAppAsyncLSP里面会post一个10s的message在时间到了的时候调用freezeProcess去冻结进程(延时10s发送进程冻结的消息,在10s内如果收到进程解冻的消息,会把进程冻结消息移除,也就不会执行进程冻结的操作)

 @VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 10_000L; //默认超时时间,毫秒为单位,也就是10s @VisibleForTesting volatile long mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT; @GuardedBy({\"mAm\", \"mProcLock\"}) void freezeAppAsyncLSP(ProcessRecord app) { freezeAppAsyncLSP(app, updateEarliestFreezableTime(app, mFreezerDebounceTimeout)); } @GuardedBy({\"mAm\", \"mProcLock\"}) private void freezeAppAsyncLSP(ProcessRecord app, @UptimeMillisLong long delayMillis) { freezeAppAsyncInternalLSP(app, delayMillis, false); } @GuardedBy({\"mAm\", \"mProcLock\"}) void freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis, boolean force) { ... mFreezeHandler.sendMessageDelayed( mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app), delayMillis); //延迟10s发送Message ...  }  ... @Override public void handleMessage(Message msg) { switch (msg.what) { case SET_FROZEN_PROCESS_MSG: {  ProcessRecord proc = (ProcessRecord) msg.obj;  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,\"freezeProcess\");  synchronized (mAm) { freezeProcess(proc); //响应TRACE_TAG_ACTIVITY_MANAGER 调用freezeProcess  }  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  if (proc.mOptRecord.isFrozen()) { //进程冻结成功 onProcessFrozen(proc); //进程被冻结后收到的回调,执行内存压缩的相关操作 removeMessages(DEADLOCK_WATCHDOG_MSG); sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS); ///延时1s发送消息检查文件锁的持有情况, //考虑到一些特殊场景下,进程在被冰冻的过程中拿住了文件锁,冰冻成功后还会再检查一次,发现持有锁就立刻解冻。 //去冻结的时候也会去检查他是否拿有文件锁  } } break;  ... 

总结上面条件就是,通过判断冻结功能是否开启、应用是否属于豁免的应用、应用是否已经被冻结、应用是否不应该被冻结。当做完基础的判断之后,然后看应用当前的 adj 是否大于等于 900 (CACHE_APP) 来决定是否冻结应用,然后开启一个延迟10s,如果这个10s内状态都没有变化,就执行冻结流程。

tips1.DEFAULT_FREEZER_DEBOUNCE_TIMEOUT在Android 14之前是十分钟,Android 14上面是十秒**2.这个时间可以动态的修改: adb shell device_config put activity_manager_native_boot freeze_debounce_timeout 1000 这个是改成1s**3.冻结的时候会去检查文件锁状态,这是为了防止冰冻进程持有文件锁引起死锁。考虑到一些特殊场景下,进程在被冰冻的过程中拿住了文件锁,冰冻成功后还会再检查一次,发现持有锁就立刻解冻。**
2.8  CachedAppOptimizer.freezeProcess

最终都会调用到freezeProcess函数,Android 13以后针对binder 调用进行了优化,对进程的 binder 先进行冻结,这一步禁掉该进程对同步binder请求的接收和处理,以及对异步binder请求的处理,因为之前如果随意的冻结应用,会导致一些应用后台的跨进程行为异常,例如在binder通信期间。

到了Android 13之后,binder驱动已经支持FREEZER状态,当应用调用一个被冻结binder进程后,会直接返回一个错误,这样子就不会阻塞调用方的进程。

 @GuardedBy({\"mAm\"}) private void freezeProcess(final ProcessRecord proc) { ... Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,\"freezeBinder:\" + name);  if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) { //对进程的 binder 先进行冻结 handleBinderFreezerFailure(proc, \"outstanding txns\"); return;  }  ... try {  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,\"setProcessFrozen:\" + name);  traceAppFreeze(proc.processName, pid, -1);  Process.setProcessFrozen(pid, proc.uid, true); //根据uid,pid对进程执行冻结 ... }
2.9.1 freezeBinder

freezeBinder最终调用到libbinder里面去,最终判断是否是Android设备,是的话就通过ioctl,传递BINDER_FREEZE,最终走到内核的 binder 驱动上。

 /* frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java */ public static native int freezeBinder(int pid, boolean freeze, int timeoutMs); /* frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp */ static const JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ ... {\"freezeBinder\", \"(IZI)I\", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder}, ... static jint com_android_server_am_CachedAppOptimizer_freezeBinder(JNIEnv* env, jobject clazz,jint pid, jboolean freeze,jint timeout_ms) { jint retVal = IPCThreadState::freeze(pid, freeze, timeout_ms); if (retVal != 0 && retVal != -EAGAIN) { jniThrowException(env, \"java/lang/RuntimeException\", \"Unable to freeze/unfreeze binder\"); } return retVal;} /* frameworks/native/libs/binder/IPCThreadState.cpp*/status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { struct binder_freeze_info info; int ret = 0; info.pid = pid; info.enable = enable; info.timeout_ms = timeout_ms;#if defined(__ANDROID__) //判断是否是Android设备 if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0) // 通过ioctl,传递BINDER_FREEZE,走到内核的 binder 驱动上 ret = -errno;#endif
tipsioctl(input/output control)是一个专用于设备输入输出操作的系统调用,该调用传入一个跟设备有关的请求码,系统调用的功能完全取决于请求码。
2.9.2 setProcessFrozen

setProcessFrozen是一个native函数,会调用到android_util_Process的android_os_Process_setProcessFrozen函数,在此函数里会调用cgroup中间抽象层libprocessgroup的API,通过cgroup本身的freezer子系统来实现进程冻结功能。

 /* frameworks/base/core/java/android/os/Process.java */ /** * Freeze or unfreeze the specified process. * * @param pid Identifier of the process to freeze or unfreeze. * @param uid Identifier of the user the process is running under. * @param frozen Specify whether to free (true) or unfreeze (false). * * @hide */ public static final native void setProcessFrozen(int pid, int uid, boolean frozen); ... /* frameworks/base/core/jni/android_util_Process.cpp */ static const JNINativeMethod methods[] = { {\"setProcessFrozen\", \"(IIZ)V\", (void*)android_os_Process_setProcessFrozen}, } .... void android_os_Process_setProcessFrozen( JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze){ bool success = true; if (freeze) { success = SetProcessProfiles(uid, pid, {\"Frozen\"}); } else { success = SetProcessProfiles(uid, pid, {\"Unfrozen\"}); } if (!success) { signalExceptionForGroupError(env, EINVAL, pid); }}

3. 总结

通过上面的触发流程可以看出,系统通过

adj的变化来触发冻结状态的更新。经过一系列判断后,从 Java 层到 Native 层,最终调用到 Cgroup 中间抽象层的 API,进而通过 Cgroup 实现进程的冻结功能,方法执行流程如下:

4. 遇到的问题

4.1.多次点击侧边栏发生、便签应用发生闪退

解决思路:

1. 查看全部打印发现这两个应用有被冻结的日志,会有如下日志:

ActivityManager: freezing xxx xxx

2. 怀疑应用还在使用时就被冻结,导致无法收到点击事件而进行分发,进而出现ANR,那么我们设置如下属性值,disabled将冻结功能关闭,再去按照同样的手法尝试复现问题,经过多次尝试发现问题未出现,证明该问题是这两个应用被冻结导致。

adb shell settings put global cached_apps_freezer 

3. 回顾上述流程,可以知道在CachedAppOptimizer.java类中的freezeProcess会对进程进行冻结操作,只需要在进行冻结前通过包名将这两个应用过滤掉就解决了

 /** * Freeze a process. * @param proc process to be frozen */ @GuardedBy({\"mAm\"}) private void freezeProcess(final ProcessRecord proc) { int pid = proc.getPid(); // Unlocked intentionally final String name = proc.processName; final long unfrozenDuration; final boolean frozen; final ProcessCachedOptimizerRecord opt = proc.mOptRecord; if(\"xxxxx\".equals(name)){ //通过包名进行过滤  Slog.d(TAG_AM,\"do not freeze \"+name);  return; } synchronized (mProcLock) { // someone has canceled this freeze ... }
4.2 反馈悬浮窗顶部含有title的整个相对布局概率性消失

解决思路:

1. 查看悬浮窗的布局文件,并未发现任何异常,而且该应用一直迭代基本未进行修改,且布局简单直接排除应用的影响。

2. 有了上次过滤应用进入冻结的经验,同样尝试在CachedAppOptimizer.java类中将反馈应用的包名过滤掉,按照问题复现手法复现,发现仍然存在上述问题,但我不死心,仍然觉得这和冻结有关,直接设置如下属性将冻结功能关闭:

adb shell settings put global cached_apps_freezer disabled

3.再按照复现手法复现发现问题得到解决,这下就有点迷了,已经防止应用进入冻结了,为什么还会出现消失的问题呢,于是查看打印发现应用被冻结后会在适当的时机进行解冻,然后在我打开反馈的时候过滤unfree字段,如下:

logcat | grep -i unfree

4.当时查看打印果然发现打开的时候反馈应用有被unfreeze,在上面的流程中OomAdjuster.java类中的updateAppFreezeStateLSP有免于参与冻结的过滤条件,如下:

@GuardedBy({\"mService\", \"mProcLock\"}) private void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) { if (!mCachedAppOptimizer.useFreezer()) { return; } //在这里通过包名过滤,打开应用时就不会被unfreeze了 final String packageName = app.processName if (app.mOptRecord.isFreezeExempt() || \"xxxxx\".equals(processName)) { return; } final ProcessCachedOptimizerRecord opt = app.mOptRecord; // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze if (opt.isFrozen() && opt.shouldNotFreeze()) { mCachedAppOptimizer.unfreezeAppLSP(app,  CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); return; } ...}

5.至此,问题得到解决

参考文档:

基于AndroidU进程冻结机制详解_android freeze-CSDN博客