activityIdleInternal() 的主要任务是改变系统中 Activity 的状态信息,并将其添加到不同状态列表中。
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (allResumedActivitiesIdle()) {
if (r != null) {
mService.scheduleAppGcsLocked(); //scheduleAppGcsLocked
}
}
//处理需要finish的Activity
for (int i = 0; i < NF; i++) {
r = finishes.get(i);
final ActivityStack stack = r.task.stack;
if (stack != null) {//
activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
}
}
mService.trimApplications(); //实际处理内存 trimApplications 最终会调到udateOomAdjLocked
if (activityRemoved) {
resumeFocusedStackTopActivityLocked();
}
return r;
}
ActivityStackSupervisor:activityIdleInternalLocked其主要工作如下:
调用 scheduleAppGcsLocked() 方法通知所有进行中的任务进行垃圾回收。scheduleAppGcsLocked() 将进行调度 JVM 的 garbage collect,回收一部分内存空间,这里仅仅是通知每个进程自行进程垃圾检查并调度回收时间,而非同步回收。处理需要finish 和需要stop的Activity 对应NS NF。
scheduleAppGcsLocked 该方法分别在以下情况被调用
- ActivityStackSupervisor:activityIdleInternalLocked每次会调用scheduleAppGcsLocked;
- AMS:doLowMemReportIfNeededLocked lowmemory时候
- ActivityRecord:windowsVisibleLocked window可见变化
- BroadcastQueue:processNextBroadcast 处理完广播 size = 0
调用scheduleAppGcsLocked流程 最终走向 ActivityThread handleLowMemory()调用各级onLowMemory(),以及AT中的GCIdler。
判断mProcessesToGc数量大于0,发送GC_BACKGROUND_PROCESSES_MSG
final void scheduleAppGcsLocked() {
if (mProcessesToGc.size() > 0) {
ProcessRecord proc = mProcessesToGc.get(0);
//判断mProcessesToGc数量大于0,发送GC_BACKGROUND_PROCESSES_MSG
Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);
long when = proc.lastRequestedGc + GC_MIN_INTERVAL;
long now = SystemClock.uptimeMillis();
if (when < (now+GC_TIMEOUT)) {
when = now + GC_TIMEOUT;
}
mHandler.sendMessageAtTime(msg, when);
}
}
final void performAppGcsIfAppropriateLocked() {
if (canGcNowLocked()) { //判断是否可以GC,根据广播 sleep 等判断
performAppGcsLocked();
return;
}
// Still not idle, wait some more.
scheduleAppGcsLocked(); //不可以则等待
}
performAppGcsLocked(),根据时间判断当前时间和上次时间+GC默认间隔做判断
final void performAppGcsLocked() {
if (canGcNowLocked()) {
while (mProcessesToGc.size() > 0) {
ProcessRecord proc = mProcessesToGc.remove(0);
if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory){
//如果上次GC时间 + 最小GC间隔 小于等于 现在时间
if ((proc.lastRequestedGc+GC_MIN_INTERVAL)<= SystemClock.uptimeMillis()) {
// To avoid spamming the system, we will GC processes one
// at a time, waiting a few seconds between each.
performAppGcLocked(proc); //准备GC 这里最终会调到AT中的GCIdler
scheduleAppGcsLocked();//等待
return;
}
}
}
}
}
performAppGcLocked中会判断当前进程是否LowMemory,如果是则会走ActivityThread handleLowMemory() ,否则对应ActivityThread中scheduleGcIdler()
final void performAppGcLocked(ProcessRecord app) {
try {
app.lastRequestedGc = SystemClock.uptimeMillis();
if (app.thread != null) {
if (app.reportLowMemory) {
app.reportLowMemory = false;
app.thread.scheduleLowMemory(); //ActivityThread的 handleLowMemory()
} else {
app.thread.processInBackground();
}
}
} catch (Exception e) {
}
}
AT#handleLowMemory() 最终调用到各级执行onLowMemory回调
处理了各级onLowMemory的回调,释放非system的sqlite 释放service,调用GC
final void handleLowMemory() {
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);
final int N = callbacks.size();
for (int i=0; i<N; i++) {
callbacks.get(i).onLowMemory();//各级执行onLowMemory回调
}
// Ask SQLite to free up as much memory as it can, mostly from its page caches.
if (Process.myUid() != Process.SYSTEM_UID) { //释放sqlite
int sqliteReleased = SQLiteDatabase.releaseMemory();
EventLog.writeEvent(SQLITE_MEM_RELEASED_EVENT_LOG_TAG, sqliteReleased);
}
// Ask graphics to free up as much as possible (font/image caches)
Canvas.freeCaches();//释放canvas
// Ask text layout engine to free also as much as possible
Canvas.freeTextLayoutCaches();
BinderInternal.forceGc("mem"); // 注意这里
}
上面performAppGcLocked(proc)最终会调到AT#scheduleGcIdler(),再消息队列里添加一个mGcIdler,mGcIdler是一个IdleHandler
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
Looper.myQueue().addIdleHandler(mGcIdler);
}
}
接下来可以看下doGcIfNeeded 根据上次GC的时间加上两次GC间隔的最小时间5s,判断当前是否要GC
void doGcIfNeeded() {
mGcIdlerScheduled = false;
final long now = SystemClock.uptimeMillis();
if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) {
BinderInternal.forceGc("bg");
}
}
2.2. 回收过程函数 trimApplications()
trimApplications()在系统中调用地方
- ActivityStackSupervisor: activityIdleInternalLocked
- AMS:activityStopped
- AMS:setProcessLimit
- AMS:unregisterReceiver
- AMS:finishReceiver
trimApplications() 函数的结构如下 :
mRemovedProcesses 列表中主要包含了 crash 的进程、5 秒内没有响应并被用户选在强制关闭的进程、以及应用开发这调用 killBackgroundProcess 想要杀死的进程。调用 Process.killProcess 将所有此类进程全部杀死。
final void trimApplications() {
synchronized (this) {
int i;
// First remove any unused application processes whose package has been removed.
for (i=mRemovedProcesses.size()-1; i>=0; i--) {
final ProcessRecord app = mRemovedProcesses.get(i);
if (app.activities.size() == 0 && app.curReceiver == null && app.services.size() == 0) {
if (app.pid > 0 && app.pid != MY_PID) {
app.kill("empty", false);
} else {
app.thread.scheduleExit();
}
cleanUpApplicationRecordLocked(app, false, true, -1, false/*replacingPid*/);
mRemovedProcesses.remove(i);
if (app.persistent) {
addAppLocked(app.info, false, null /* ABI override */);
}
}
}
// Now update the oom adj for all processes.
updateOomAdjLocked();
}
}
从上面代码中可以看出,进程被杀死的条件是:
- 必须是非 persistent 进程,即非系统进程;
- 必须是空进程,即进程中没有任何 activity 存在。如果杀死存在 Activity 的进程,有可能关闭用户正在使用的程序,或者使应用程序恢复的时延变大,从而影响用户体验;
- 必须无 broadcast receiver。运行 broadcast receiver 一般都在等待一个事件的发生,用户并不希望此类程序被系统强制关闭;
- 进程中 service 的数量必须为 0。存在 service 的进程很有可能在为一个或者多个程序提供某种服务,如GPS 定位服务。杀死此类进程将使其他进程无法正常服务。
2.3 updateOomAdjLocked
AMS相关各种操作都会碰到这个调用,主要在做了两个事情 一个就是更新各个进程adj, 另一个是最终调用到onTrimMemory;
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 | 500 | 服务进程 |
HOME_APP_ADJ | 600 | Home进程 |
PREVIOUS_APP_ADJ | 700 | 上一个进程 |
SERVICE_B_ADJ | 800 | B List中的Service |
CACHED_APP_MIN_ADJ | 900 | 不可见进程的adj最小值 |
CACHED_APP_MAX_ADJ | 906 | 不可见进程的adj最大值 |
final boolean updateOomAdjLocked(ProcessRecord app) {
final int cachedAdj = app.curRawAdj >= ProcessList.CACHED_APP_MIN_ADJ
? app.curRawAdj : ProcessList.UNKNOWN_ADJ;
//判断是否updateOomAdj成功 当需要杀掉目标进程则返回false;否则返回true。
boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,SystemClock.uptimeMillis());
if (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ) {
// Changed to/from cached state, so apps after it in the LRU list may also be changed.
updateOomAdjLocked();
}
return success;
}
app.thread.scheduleTrimMemory
final void updateOomAdjLocked() {
} else {
if (mLowRamStartTime != 0) {
mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
mLowRamStartTime = 0;
}
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
}
if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
|| app.systemNoUi) && app.pendingUiClean) {
if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
&& app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
"Trimming memory of ui hidden " + app.processName
+ " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
app.thread.scheduleTrimMemory(
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
} catch (RemoteException e) {
}
}
app.pendingUiClean = false;
}
app.trimMemoryLevel = 0;
}
}
}
3.onLowMemory、 onTrimMemory优化
OnTrimMemory的主要作用就是指导应用程序在不同的情况下进行自身的内存释放,以避免被系统直接杀掉,提高应用程序的用户体验
OnTrimMemory:Android 4.0之后提供的API,系统会根据不同的内存状态来回调。根据不同的内存状态,来响应不同的内存释放策略。
onLowMemory()方法在使用过程只要低内存状态下,就会回调.
OnLowMemory:Android提供的API,在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory。
onTrimMemory传入的几个内存级别释放内存:
- TRIM_MEMORY_RUNNING_MODERATE 你的应用正在运行,并且不会被杀死,但设备已经处于低内存状态,并且开始杀死LRU缓存里的内存。(后台进程超过5个),并且该进程优先级比较高,需要清理内存
- TRIM_MEMORY_RUNNING_LOW 你的应用正在运行,并且不会被杀死,但设备处于内存更低的状态,所以你应该释放无用资源以提高系统性能(直接影响app性能)
- TRIM_MEMORY_RUNNING_CRITICAL 你的应用还在运行,但系统已经杀死了LRU缓存里的大多数进程,所以你应该在此时释放所有非关键的资源。如果系统无法回收足够的内存,它会清理掉所有LRU缓存,并且开始杀死之前优先保持的进程,像那些运行着service的。(后台进程不足3个),并且该进程优先级比较高,需要清理内存
- TRIM_MEMORY_BACKGROUND 系统运行在低内存状态,并且你的进程已经接近LRU列表的顶端(即将被清理).虽然你的app进程还没有很高的被杀死风险,系统可能已经清理LRU里的进程,你应该释放那些容易被恢复的资源,如此可以让你的进程留在缓存里,并且当用户回到app时快速恢复.该进程是后台进程。
- TRIM_MEMORY_MODERATE 系统运行在低内存状态,你的进程在LRU列表中间附近。如果系统变得内存紧张,可能会导致你的进程被杀死。并且该进程在后台进程列表的中部。
- TRIM_MEMORY_COMPLETE 系统运行在低内存状态,如果系统没有恢复内存,你的进程是首先被杀死的进程之一。你应该释放所有不重要的资源来恢复你的app状态。该进程在后台进程列表最后一个,马上就要被清
- TRIM_MEMORY_UI_HIDDEN:内存不足,并且该进程的UI已经不可见了。
onLowMemory、 onTrimMemory优化,需要释放什么资源?
在内存紧张的时候,会回调OnLowMemory/OnTrimMemory,需要在回调方法中编写释放资源的代码。 可以在资源紧张的时候,释放UI 使用的资源资:Bitmap、数组、控件资源。 注意回调时刻: OnLowMemory被回调时,已经没有后台进程;而onTrimMemory被回调时,还有后台进程。 OnLowMemory是在最后一个后台进程被杀时调用,一般情况是low memory killer 杀进程后触发;而OnTrimMemory的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发。 在Application、 Activity、Fragement、Service、ContentProvider中都可以重写回调方法,对OnLowMemory/OnTrimMemory进行回调,在回调方法中实现资源释放的实现。 以Activity为例,在Activity源码中能够看到对于onTrimMemory的定义,因此在回调的时候重写方法即可。
4.BinderInternal
BinderInternal内部有sGcWatchers对应一个runnable list,BinderInternal重写了finalize()方法。
根据JVM的原理,JVM垃圾回收器准备释放内存前,会先调用该对象finalize。
当执行GC的时候,会依次执行每个runnabe的run()方法,
并根据具体内存情况(3/4 davilk memory)进行操作。
public class BinderInternal {
static WeakReference<GcWatcher> sGcWatcher
= new WeakReference<GcWatcher>(new GcWatcher());
static ArrayList<Runnable> sGcWatchers = new ArrayList<>(); //
static Runnable[] sTmpWatchers = new Runnable[1]; //数组
static long sLastGcTime;
static final class GcWatcher {
@Override
protected void finalize() throws Throwable {
handleGc();
sLastGcTime = SystemClock.uptimeMillis();
synchronized (sGcWatchers) {
sTmpWatchers = sGcWatchers.toArray(sTmpWatchers);
}
for (int i=0; i<sTmpWatchers.length; i++) {
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**
**因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/b8c4a915e850e1390203cf3125903d5f.png)
![img](https://img-blog.csdnimg.cn/img_convert/c8d3ee5e51ee719301ced6232840121a.png)
![img](https://img-blog.csdnimg.cn/img_convert/9243e8747aecd36dceee23ab61c4a94e.png)
![img](https://img-blog.csdnimg.cn/img_convert/5543e5f822e3bbe7ff65e937dd984ff7.png)
![img](https://img-blog.csdnimg.cn/img_convert/b86136890bcfc220ce8accce8f6cb6a9.png)
![img](https://img-blog.csdnimg.cn/img_convert/86b1287f476af1d7248d03c9bf1099a8.png)
![img](https://img-blog.csdnimg.cn/13f2cb2e05a14868a3f0fd6ac81d625c.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!**
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
**如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)**
![img](https://img-blog.csdnimg.cn/img_convert/e7f26dfd5e28eeef596b110c2dbe54f2.png)
### 总结
**写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的
还有高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。**
![跨平台开发:Flutter.png](https://img-blog.csdnimg.cn/img_convert/5a7ba99bf5c144faeb019b8c4481353f.webp?x-oss-process=image/format,png)
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
![img](https://img-blog.csdnimg.cn/img_convert/248d411b18919811ef5ac0c5b375a1d6.png)
习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的
还有高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。**
[外链图片转存中...(img-uAGLm0Cj-1712879293227)]
**一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
[外链图片转存中...(img-CR4YmJLq-1712879293227)]