Launcher的启动过程
前言
Launcher即桌面,是Android智能设备的窗口,用户使用最频繁的软件之一。Launhcer是Android所有应用的入口,也提供窗口小部件等功能。如下图:
当然,Launcher本身就是一个APP,一个提供桌面的APP,我们也可以开发一款Launcher APP作为手机的桌面。Laucher有很多和普通APP不同的地方。
- Launcher是顶部APP,即任何应用返回后都是到Launcher,不能再继续返回;
- Launcher是所有应用的入口,可以管理应用;
- Launcher是Android系统启动后就要显示给用户的应用。
我们的手机一开机第一眼看见的就是Launcher,也就是说Launcher在开机的过程中就已经启动完成,下文,我们就来看看
- Launcher的在开机时的启动过程;
- 每次按Home键的启动过程(上图中底部中间圆圈的按钮);
- 以及Launcher发生强退等应用退出等意外情况的启动过程。
开机Launcher的启动
手机开机时,会启动ActivityManagerService,在这个系统服务启动完成后,便会启动Launcher。关于ActivityManagerService启动过程的详细过程,读者可以参阅文章《 Android系统之System Server大纲 》。
启动时序图
ActivityManagerService启动完成后,会调用systemReady()方法,如下:
public void systemReady(final Runnable goingCallback) {
synchronized(this) {
.....
startHomeActivityLocked(currentUserId, "systemReady");
......
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。
如上面的代码,ActivityManagerService启动完成后调用systemReady()方法,然后调用startHomeActivityLocked()方法,启动HomeActivity,即Launcher应用的桌面Activity。继续看这个方法
boolean startHomeActivityLocked(int userId, String reason) {
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instrumentationClass == null) {
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
}
}
return true;
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。
上面的代码首先是调用了getHomeIntent()方法取得HomeActivity的Intent,看这个方法的实现
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
return intent;
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。
上面的代码,new一个Intent对象,mTopAction的值是Intent.ACTION_MAIN,我们重点关注intent.addCategory(Intent.CATEGORY_HOME)这条语句,给HomeActvity 的 intent 对象添加Category Intent.CATEGORY_HOME=android.intent.category.HOME,也就是说Launcher的Activity必须声明Category android.intent.category.HOME,即表明这个Actiivty是一个Home Activity。
取得Intent返回startHomeActivityLocked()方法,通过方法resolveActivityInfo()遍历手机中所有安装包含Category Intent.CATEGORY_HOME=android.intent.category.HOME的Activity,然后把Activity的信息作为Component通过intent.setComponent()方法传输给intent对象。如果是Android原生的手机,此时aInfo.applicationInfo.packageName的值是:com.android.launcher3,aInfo.name的值是:com.android.launcher3.Launcher,即com.android.launcher3.Launcher(Android 7.0)是Home Activity。
然后调用mActivityStarter.startHomeActivityLocked()继续启动Home Activity。
void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
startActivityLocked(null /*caller*/, intent, ......);
if (mSupervisor.inResumeTopActivity) {
mSupervisor.scheduleResumeTopActivities();
}
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityStarter.java中。
首先通过mSupervisor.moveHomeStackTaskToTop()方法把Launcher的堆栈移到顶部,这也是为什么Launcher总是在所有APP的顶部的原因。然后接着调用startActivityLocked()继续启动Home Activity。startActivityLocked()是Android系统启动所有Activity的入口,本文就不再阐述Activity的启动过程了。
按Home键的Launcher启动过程
时序图
既然是按Home键,那么就涉及到输入系统,Home是一个按键,当被按下时,底层会上报事件到InputManagerService,如下
// Native callback.
private long interceptKeyBeforeDispatching(InputWindowHandle focus,
KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/input/InputManagerService.java中。
参数event封装了这次按键事件,接着调用了WindowManagerCallbacks即InputMonitor的interceptKeyBeforeDispatching方法
public long interceptKeyBeforeDispatching(
InputWindowHandle focus, KeyEvent event, int policyFlags) {
WindowState windowState = focus != null ? (WindowState) focus.windowState : null;
return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java中。
没有做任何处理,直接调用了mService.mPolicy.interceptKeyBeforeDispatching(),mPolicy实质是PhoneWindowManager对象
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
......
if (keyCode == KeyEvent.KEYCODE_HOME) {
......
handleShortPressOnHome();
return -1;
}
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java中。
如上面的代码,keyCode是KeyEvent.KEYCODE_HOME时,调用处理短按home键handleShortPressOnHome()的方法
private void handleShortPressOnHome() {
......
// Go home!
launchHomeFromHotKey();
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java中。
继续往下执行launchHomeFromHotKey()
void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
......
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java中。
首先调用sendCloseSystemWindows()关闭系统弹窗,调用startDockOrHome()启动Dock或者Home,本文要论述的是如何启动Home。
void startDockOrHome(boolean fromHomeKey, boolean awakenFromDreams) {
......
if (fromHomeKey) {
intent = new Intent(mHomeIntent);
intent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);
} else {
intent = mHomeIntent;
}
startActivityAsUser(intent, UserHandle.CURRENT);
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java中。
创建Home Activity的Intent,mHomeIntent详情如下
mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
mHomeIntent.addCategory(Intent.CATEGORY_HOME);
mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
如上文中提到,Home Activity的必须是一个携带Category为Intent.CATEGORY_HOME的Activity。在启动Activity之前,往Intent中设置WindowManagerPolicy.EXTRA_FROM_HOME_KEY/fromHomeKey的键值对,表明从Home按键启动Launcher。然后调用startActivityAsUser()方法,把Home Activity启动起来。
意外情况启动Launcher
所谓意外情况,比如Launcher强退等异常。Launcher也是一个普通的应用,当发生异常导致强退时,也就是说Launcher死掉了,那么由Launcher显示的桌面也没有了。但是,如果桌面都没有了,用户还怎么使用Launcher呢?所以,当Launcher发生异常等导致强退时,系统需要自动重新把Launcher启动起来。
时序图
以强退为例,我们知道Android的Activity由ActivityManagerService管理和维护者,当一个应用发生强退,会调用到ActivityManagerService的forceStopPackage()方法。
当应用强退时,调用ActivityManagerService的forceStopPackage()方法
public void forceStopPackage(final String packageName, int userId) {
if (isUserRunningLocked(user, false)) {
forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
}
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。
如上面的代码,继续调用forceStopPackageLocked()方法,最终会调用scheduleIdleLocked()方法处理强退。
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
......
if (activityRemoved) {
resumeTopActivitiesLocked();
}
return r;
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java中。
如上面的方法,从scheduleIdleLocked()一直到activityIdleInternalLocked(),再往下处理Activity挂掉的动作。
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
return isOnHomeDisplay() &&
mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason);
}
这个方法定义在文件frameworks/base/services/core/java/com/android/server/am/ActivityStack.java中。
如上面的方法,如果挂掉的Activity是Home Activity,那么调用resumeHomeStackTask()重新启动Launcher。最后调用ActivityManagerService的startHomeActivityLocked()方法,这里和开机启动的Launcher的过程一样了。更详细的流程请查看时序图。