am Android系统的活动管理器, 系统使用AM service管理应用的四大组件, 是Android系统的大管家. 弄明白大管家的工作机制, 方便我们与其打交道. 在阐述工作原理的同时,提供一些工具的使用.
前言
ActivityManagerService是一个很庞大的模块, 接口相对简单很多, 通过IActivityManager.aidl的接口定义弄明白对外提供了哪些服务(我们关心的服务, 关心的话可以自行搜索):
操控对象 | 相关函数 | 调用者 | 作用 |
activity | startActivity | Instrumentation.startActivity | 启动Activity |
finishActivity | 由Activity的finish调用 | finishActivity | |
activityIdle | 有ActivityThread添加IdleHandler触发 | ||
activityPaused/Stoped | 由ActivityThread在performActivityPaused后调用 | ||
application | handleApplicationCrash | RuntimeInit.java 的KillApplicationHandler | 显示Crash对话框 |
attachApplication | 由ActivityThread启动时调用 | 表明ActivityThread准备就绪 | |
task | moveTaskToFront | ActivityManager调用 | |
moveTaskBackwards | none | ||
getTaskForActivity | Activity自身调用 | ||
configuration | updateConfiguration | ||
Alarm | noteWakeupAlarm |
Activity相关的
跟Activity相关的任务是TaskRecord, ActivityStack, ActivityStackSupervisor这三个类, 其中ActivityStackSupervisor是Activity管理的大总管, 其直接管理的对象是多个地区经理ActivityStack. ActivityStack管理本地区的多个店长TaskRecord, 每个TaskRecord记录管理多个店员. 对了还有一个ActivityStarter, 这是一个HR, 用于招聘安置店员的.
大总管只有一个, 是ActivityManagerService初始化时直接new出来的。
地区经理ActivityStack
固定的地区经理ActivityStack
- HOME_STACK_ID: 0, Home activity stack ID
- FULLSCREEN_WORKSPACE_STACK_ID: 1, 全屏模式的Activity启动位置
- FREEFORM_WORKSPACE_STACK_ID: 2,
- DOCKED_STACK_ID: 3,
- PINNED_STACK_ID: 4, 这个是跟画中画有关的一个特性
- RECENTS_STACK_ID: 5, 最近任务
能够构造地区经理ActivityStack的方法是ActivityStackSupervisor.createStackOnDisplay, 具体调用来源:
ActivityManagerService.createStackOnDisplay:
- 通过调用adb shell am stack start <DISPLAY_ID>
启动
- 通过调用adb shell am stack start <DISPLAY_ID>
ActivityStackSupervisor.getStack(stackId, createStaticStaticStackIfNeeded, createOnTop): 按需创建
- ActivityManagerService.positionTaskInStack(taskId, stackId, position)
- ActivityStackSupervisor.setWindowManager时会构造mHomeStack
- ActivityStackSupervisor.restoreRecentTaskLocked
- ActivityStackSupervisor.moveTopStackActivityToPinnedStackLocked # pictureMode
- ActivityStarter.getLauchStack 获取启动Activity的所属Stack, 按需创建RECENT_STACK_ID, ASSISTANT_STACK_ID, 以及其他.
ActivityStackSupervisor.getValidLauchStackOnDisplay(displayId, r): 不是默认屏幕的话会按需创建一个
adb命令查看当前的Stack信息
在手机刚启动完成后查看ActivityStack信息列表:
$ adb shell am stack list
Stack id=0 bounds=[0,0][1440,2560] displayId=0 userId=0
taskId=1276: com.android.launcher3/com.android.launcher3.Launcher bounds=[0,0][1440,2560] userId=0 visible=true topActivity=ComponentInfo{com.android.launcher3/com.android.launcher3.Launcher}
可以看到0是Home Stack ID
打开一个App后
$ adb shell am stack list
Stack id=1 bounds=[0,0][1440,2560] displayId=0 userId=0
taskId=1277: com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity bounds=[0,0][1440,2560] userId=0 visible=true topActivity=ComponentInfo{com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity}
Stack id=0 bounds=[0,0][1440,2560] displayId=0 userId=0
taskId=1276: com.android.launcher3/com.android.launcher3.Launcher bounds=[0,0][1440,2560] userId=0 visible=false topActivity=ComponentInfo{com.android.launcher3/com.android.launcher3.Launcher}
退出这个App后Stack信息又变回了刚启动时的样子。 可以看出ActivityStack被及时销毁了.
查看过近期任务列表后:
Stack id=0 bounds=[0,0][1440,2560] displayId=0 userId=0
taskId=1276: com.android.launcher3/com.android.launcher3.Launcher bounds=[0,0][1440,2560] userId=0 visible=true topActivity=ComponentInfo{com.android.launcher3/com.android.launcher3.Launcher}
Stack id=5 bounds=[0,0][1440,2560] displayId=0 userId=0
taskId=1280: com.android.systemui/com.android.systemui.recents.RecentsActivity bounds=[0,0][1440,2560] userId=0 visible=false topActivity=ComponentInfo{com.android.systemui/com.android.systemui.recents.RecentsActivity}
可以看到id=5是最近任务列表Stack
关于地区ActivityStack一些问题解答
ActivityStack与Display一一绑定, 可以换所属地区吗?
答案是可以, mDisplayId这个属性可以重新赋值, 可以从显示器中removeFromDisplay, 然后postAddToDisplay即可完成地区的转移. 我们是可以在不同显示屏中拖拽页面的.
店长TaskRecord(店员ActivityRecord)
店长TaskRecord是由地区经理ActivityStack的createTaskRecord创建的. 正常流程下, 由setTaskFromReuseOrCreateNewTask, setTaskToCurrentTopOrCreateNewTask调用, 均在ActivityStarter中.
ActivityStarter这个HR一次只会招聘一个人, 在招聘过程中, 会根据需要协助创建店长TaskRecord. 创建TaskRecord的位置:
- ActivityManagerService.addAppTask, 通过调用ActivityManager调用
- TaskRecord restoreFromXml 用于重启后状态恢复
- ActivityStack.createTaskRecord
- resetTargetTaskIfNeededLocked
- moveActivityToPinnedStackLocked 画中画有关的, 暂时忽略
- ActivityStarter.setTaskFromReuseOrCreateNewTask 由ActivityStarter.startActivityUnchecked 调用
- ActivityStarter.setTaskToCurrentTopOrCreateNewTask 由ActivityStarter.startActivityUnchecked调用
维护TaskRecord的关键函数是ActivityStarter.startActivityUnchecked(实际上也是招聘店员的过程). 流程图如下入:
除了需要调用WM服务的ActivityStack.moveToFront, 在ActivityRecord构造完成,并计入TaskRecord后(可以理解为入职后)。 大总管ActivityStackSupervisor会通过 resumeFocusedStackTopActivityLocked 方法将对应的Activity推到前台显示, 直接操作当然是ActivityStack完成的. 动画Transaction是这个过程做的.
Activity的启动过程
通过源码阅读跟踪下Activity的启动过程, 先来张时序图:
由于Activity的启动过程情况种类比较多,某些细节过于繁琐, 且整个调用过程存在多个异步调用, 这张时序图也仅仅包含了关键几个步骤, 可以参见这个时序图查看完整的代码.
- 首先Instrumentation调用am服务的startActivityAsUser, 开始通知AM服务进入Activity启动过程
- 通过ActivityStarter调用startActivity, startActivityUnchecked主要用于计算flags,对应Stack, 对应Task
- 调用对应ActivityStack的startActivityLocked方法, 将ActivityRecord这条记录插入对应的Task中(也包括对应的WM服务)
- 调用ActivityStackSupervisor的resumeFocusedStackTopActivityLocked令新Actiivty进入resume状态
这里详细说下resumeFocusedStackTopActivityLocked这个方法, 直白点说, 就是通知大总管ActivityStackSupervisor让最上层的那个Activity可见且保证进入resume状态, 接下来对应的ActivityStack作为地区经理需要做一下动作:
- 步骤1: 令上一个resume的Activity pause. 知道全部Activity pause完成(不一定)才允许接着往下走
- pause完成后通过AMS会调用ActivityStack的completePauseLocked, 调用了resumeFocusedStackTopActivityLocked
- 由于pause完成, 会跳过步骤1, 如果可以直接resume, 则这几resume 否则会调用ActivityStackSupervisor的startSpecificActivityLocked
- 若进程存活则调用realStartActivity, 若进程不存活, 则调用startProcess启动进程,进程启动后会调用attachApplication,然后调用realStartActivity
- realStartActivity中会调用scheduleLaunchActivity
Activity的销毁与恢复
销毁Activity会(不是绝对)调用destory, 对于AM而言只有一个地方调用了。 那就是ActivityStack.java的destroyActivityLocked方法
销毁两种情况: 自己调用了finish方法, 或者系统由于某些原因对activity进行了回收. ActivityThread在调用onDestroy后会调用AM的onDestroyed的方法更新状态. 而在onDestroyed中, 如果此Activity finished则从历史中(实际上就是记录在TaskRecord中)删除, 否则保留.
针对第二种情况, 保留Activity, 是如何发生的: 1. 调用了activity的releaseInstance方法. 第二种是在内存低的时候调用的:
ActivityThread.java中的看门狗:
// Watch for getting close to heap limit.
BinderInternal.addGcWatcher(new Runnable() {
@Override public void run() {
if (!mSomeActivitiesChanged) {
return;
}
Runtime runtime = Runtime.getRuntime();
long dalvikMax = runtime.maxMemory();
long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
if (dalvikUsed > ((3*dalvikMax)/4)) {
if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
+ " total=" + (runtime.totalMemory()/1024)
+ " used=" + (dalvikUsed/1024));
mSomeActivitiesChanged = false;
try {
mgr.releaseSomeActivities(mAppThread);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
});
ActivityManagerService.java
@Override
public void releaseSomeActivities(IApplicationThread appInt) {
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
try {
ProcessRecord app = getRecordForAppLocked(appInt);
mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
然后进入ActivitySupervisor中, 如果同时存在两个以上的task才会被回收一个, 最后调用了ActivityStack的releaseSomeActivities
已销毁的Activity如何恢复
在后一个Activity调用finish时,会首先调用其onPause方法, 随后(或者超时)时调用ActivityStack的ensureActivitiesVisibleLocked方法, 然后调用makeVisibleAndRestartIfNeeded代理给StackSupervisor的startSpecificActivityLocked方法发出launch指令
已销毁的Activity的onActivityResult何时调用的
文档中说onActivityResult会在onResume之前调用, 所以判断会在onCreate与onResume之间调用才会有意义. 结合上一节中最后发出了launch指令触发了ActivityThread的handleLaunchActivity。在从Activity下手, 查找调用onActivityResult的地方dispatchActivityResult,
最终找到结论: 在performResumeActivity中调用,
if (r.pendingResults != null) {
deliverResults(r, r.pendingResults);
r.pendingResults = null;
}
r.activity.performResume();
Broadcast相关的
Android有个臭名昭著的全家桶互相唤醒功能, 对Android的内存与电量是一个很大的浪费。 再次阅读AM服务, 学习广播的发送接收过程。
力求对AM服务进行改写, 彻底拦截黑名单中的广播滥用.
广播的发送
发送广播为Context.sendBroadcast其实现为ContextImpl.java:
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
可以看出最终调用了ActivityManagerService的broadcastIntent. 最终调用broadcastIntentLocked将Broadcast记录存放在Queue中.
广播的分发
在广播的发送中, 广播记录最终被记录在了mFgBroadcastQueue和mBgBroadcastQueue中.