Launcher的启动过程

96
FamilyYuan
2017.08.11 15:16* 字数 1570

Launcher的启动过程

前言

Launcher即桌面,是Android智能设备的窗口,用户使用最频繁的软件之一。Launhcer是Android所有应用的入口,也提供窗口小部件等功能。如下图:

Screenshot from 2017-03-23 19:07:26.png

当然,Launcher本身就是一个APP,一个提供桌面的APP,我们也可以开发一款Launcher APP作为手机的桌面。Laucher有很多和普通APP不同的地方。

  • Launcher是顶部APP,即任何应用返回后都是到Launcher,不能再继续返回;
  • Launcher是所有应用的入口,可以管理应用;
  • Launcher是Android系统启动后就要显示给用户的应用。

我们的手机一开机第一眼看见的就是Launcher,也就是说Launcher在开机的过程中就已经启动完成,下文,我们就来看看

  1. Launcher的在开机时的启动过程;
  2. 每次按Home键的启动过程(上图中底部中间圆圈的按钮);
  3. 以及Launcher发生强退等应用退出等意外情况的启动过程。

开机Launcher的启动

手机开机时,会启动ActivityManagerService,在这个系统服务启动完成后,便会启动Launcher。关于ActivityManagerService启动过程的详细过程,读者可以参阅文章《 Android系统之System Server大纲 》。

启动时序图

Screenshot from 2017-03-24 17:14:12.png

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启动过程

时序图

绘图1.png

既然是按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()方法。

异常启动.png

当应用强退时,调用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的过程一样了。更详细的流程请查看时序图。

Launcher