目录
一,背景介绍
二,源码分析
一,背景介绍
在Android的中,桌面应用Launcher由Launcher演变到Launcher2,再到现在的Launcher3,Google也做了很多改动。
Launcher不支持桌面小工具动画效果,Launcher2添加了动画效果和3D初步效果支持,从Android 4.4 (KK)开始Launcher默认使用Launcher3, Launcher3加入了透明状态栏,增加overview模式,可以调整workspace上页面的前后顺序,可以动态管理屏幕数量,widget列表与app list分开显示等功能。
我们主要研究android11的Launcher3的启动过程。
二,源码分析
路径:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
//......
if (bootingSystemUser) {
t.traceBegin("startHomeOnAllDisplays");
mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
t.traceEnd();
}
//......
}
ActivityTaskManagerInternal是 ActivityTaskManagerService的一个抽象类,正在的实现是在ActivityTaskManagerService中,
路径:/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@Override
public boolean startHomeOnAllDisplays(int userId, String reason) {
synchronized (mGlobalLock) {
return mRootWindowContainer.startHomeOnAllDisplays(userId, reason);
}
}
路径:/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
boolean startHomeOnAllDisplays(int userId, String reason) {
boolean homeStarted = false;
for (int i = getChildCount() - 1; i >= 0; i--) {
final int displayId = getChildAt(i).mDisplayId;
homeStarted |= startHomeOnDisplay(userId, reason, displayId);
}
return homeStarted;
}
这里要重点讲解下窗口结构层级,
1,RootWindowContainer:根窗口容器,树的根是它。通过它遍历寻找,可以找到窗口树上的窗口。它的孩子是DisplayContent。
2,DisplayContent:该类是对应着显示屏幕的,Android是支持多屏幕的,所以可能存在多个3,DisplayContent对象。上图只画了一个对象的结构,其他对象的结构也是和画的对象的结构是相似的。
4,TaskDisplayArea:它为DisplayContent的孩子,对应着窗口层次的第2层。第2层作为应用层,看它的定义:int APPLICATION_LAYER = 2,应用层的窗口是处于第2层。TaskDisplayArea的孩子是Task类,其实它的孩子类型也可以是TaskDisplayArea。而Task的孩子则可以是5,ActivityRecord,也可以是Task。
6,Tokens:它为DisplayContent的孩子,它的孩子是WindowToken。而WindowToken的孩子则为7,WindowState对象。WindowState是对应着一个窗口的。结构图中,DisplayContent不止包含一个Tokens,还有两个。其实ImeContainer也是继承自Tokens。
8,ImeContainer:它也为DisplayContent的孩子,它是输入法窗口的容器,它的孩子是WindowToken类型。WindowToken的孩子为WindowState类型,而WindowState类型则对应着输入法窗口。
9,Task:任务,它的孩子可以是Task,也可以是ActivityRecord类型。
10,ActivityRecord:是对应着应用进程中的Activity的。ActivityRecord是继承WindowToken的,它的孩子类型为WindowState。
11,WindowState:WindowState是对应着一个窗口的。
结构图中,DisplayContent有5个孩子。图中从上到下,第一个是Tokens,对应着窗口图层0、1。第二个是TaskDisplayArea,对应着窗口图层2。第三个是Tokens,对应着窗口图层3到14。第四个是ImeContainer,对应着窗口图层15到16。第五个是Tokens,对应着窗口图层17到36。
回到代码里面,startHomeOnDisplay(userId, reason, displayId) 按照物理显示屏幕情况来启动launcher桌面,也就是说android本身支持多屏幕显示。
boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
boolean allowInstrumenting, boolean fromHomeKey) {
if (!canStartHomeOnDisplayArea(aInfo, taskDisplayArea, allowInstrumenting)) {
return false;
}
mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,taskDisplayArea);
}
canStartHomeOnDisplayArea检测home activity是否支持在多屏上显示,真正执行启动home activity的为startHomeActivity
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
....
//返回一个 ActivityStarter 对象,它负责 Activity 的启动
//一系列 setXXX() 方法传入启动所需的各种参数,最后的 execute() 是真正的启动逻辑
//最后执行 ActivityStarter的execute方法
mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
.setOutActivity(tmpOutRecord)
.setCallingUid(0)
.setActivityInfo(aInfo)
.setActivityOptions(options.toBundle())
.execute(); //参考[4.3.1]
mLastHomeActivityStartRecord = tmpOutRecord[0];
final ActivityDisplay display =
mService.mRootActivityContainer.getActivityDisplay(displayId);
final ActivityStack homeStack = display != null ? display.getHomeStack() : null;
if (homeStack != null && homeStack.mInResumeTopActivity) {
//如果home activity 处于顶层的resume activity中,则Home Activity 将被初始化,但不会被恢复(以避免递归恢复),
//并将保持这种状态,直到有东西再次触发它。我们需要进行另一次恢复。
mSupervisor.scheduleResumeTopActivities();
}
}
execute为真正的启动逻辑,
路径:/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java