Android 系统启动过程(四) —— Launcher 启动过程

1,264 阅读9分钟

1. Launcher 简述

系统启动最后一步是启动 Launcher 来显示系统中已经安装的应用程序,Launcher 就是我们所说的桌面,自身也是一个应用程序。Launcher 在启动过程中会请求 PackageManagerService 返回系统中已经安装的应用程序信息,并将这些信息封装成一个快捷图标列表显示在系统屏幕上,这样用户可以通过点击这些快捷图标 icon 来启动相应的应用程序。

Launcher 是开机显示的桌面:

image.png

Launcher 的每一页桌面都是一个工作区,Launcher 有几页桌面就有几个工作区。工作区中用来显示应用程序的快捷图图标,每个工作区又分为 n*m 个单元格,每个单元格对应一个快捷图标,有n行m列,共 n*m 个图标。

本文将基于 android-14.0.0_r9 版本代码对 Launcher 启动过程进行详细讲解。

2. Launcher 启动过程

启动 Launcher 的入口为 AMS 的 systemReady 方法,它在 SystemServer 的 startOtherServices 方法中被调用:

// frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    ...
     mActivityManagerService.systemReady(() -> {
        Slog.i(TAG, "Making services ready");
        t.traceBegin("StartActivityManagerReadyPhase");
        mSystemServiceManager.startBootPhase(t, SystemService.PHASE_ACTIVITY_MANAGER_READY);
        t.traceEnd();
        ...
        );
    ...
}

注:AMS 在 SystemServer 的 startBootstrapServices 方法中被启动。

接下来看一下 AMS systemReady 方法的实现:

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
    ...
    boolean isBootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;
    if (isBootingSystemUser && !UserManager.isHeadlessSystemUserMode()) {
        t.traceBegin("startHomeOnAllDisplays");
        mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
        t.traceEnd();
    }
    ...        
}

调用 ActivityTaskManagerInternal 的 startHomeOnAllDisplays 方法,该方法在 ActivityTaskManagerService 的 LocalService 中实现:

// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public boolean startHomeOnAllDisplays(int userId, String reason) {
    synchronized (mGlobalLock) {
        return mRootWindowContainer.startHomeOnAllDisplays(userId, reason);
    }
}

调用到 RootWindowContainer 的 startHomeOnAllDisplays 方法:

// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
        boolean fromHomeKey) {
    ...
    return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
                    result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
                            allowInstrumenting, fromHomeKey),
            false /* initValue */);
}

最终进入 startHomeOnTaskDisplayArea 方法:

// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
        boolean allowInstrumenting, boolean fromHomeKey) {
    ...
    Intent homeIntent = null;
    ActivityInfo aInfo = null;
    if (taskDisplayArea == getDefaultTaskDisplayArea()) {
        // 创建 Launcher 启动所需的 Intent
        // action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME
        homeIntent = mService.getHomeIntent();
        // 获取到 Launcher Activity info
        aInfo = resolveHomeActivity(userId, homeIntent);
    } 
    ...

    // 根据查询到的 ActivityInfo 设置 homeIntent
    homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
    homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
    ...
    final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
            aInfo.applicationInfo.uid) + ":" + taskDisplayArea.getDisplayId();
            // 启动 Launcher
    mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
            taskDisplayArea);
    return true;
}

通过 ActivityTaskManagerService 的 getHomeIntent 方法创建启动 Launcher 所需的 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;
}

getHomeIntent 中创建了 Intent。并将 mTopAction 和 mTopData 传入。mTopAction 值为 Intent.ACTION_MAIN。如果系统运行模式不是低级工厂模式,则将 Category 设置为 Intent.CATEGORY_HOME。

然后将刚创建的 homeIntent 传入 resolveHomeActivity 方法通过 PackageManagerService 返回对应的 Activity info,即 LauncherActivity info:

ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
    final int flags = ActivityManagerService.STOCK_PM_FLAGS;
    final ComponentName comp = homeIntent.getComponent();
    ActivityInfo aInfo = null;
    try {
        if (comp != null) {
            // Factory test.
            aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
        } else {
            final String resolvedType =
                    homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
            // 最终会通过 PackageManagerService 查询到对应 info
            final ResolveInfo info = mTaskSupervisor.resolveIntent(homeIntent, resolvedType,
                    userId, flags, Binder.getCallingUid(), Binder.getCallingPid());
            if (info != null) {
                aInfo = info.activityInfo;
            }
        }
    } 
    ...
    aInfo = new ActivityInfo(aInfo);
    aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
    return aInfo;
}

返回到 RootWindowContainer 的 startHomeOnTaskDisplayArea 方法,接着根据查询到的 ActivityInfo 设置了 homeIntent 的 Component 、Flags 等。

最后调用 ActivityStartController 的 startHomeActivity 方法:

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,
        TaskDisplayArea taskDisplayArea) {
    ...

    mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
            .setOutActivity(tmpOutRecord)
            .setCallingUid(0)
            .setActivityInfo(aInfo)
            .setActivityOptions(options.toBundle())
            .execute();
    mLastHomeActivityStartRecord = tmpOutRecord[0];
    if (rootHomeTask.mInResumeTopActivity) {
        // If we are in resume section already, home activity will be initialized, but not
        // resumed (to avoid recursive resume) and will stay that way until something pokes it
        // again. We need to schedule another resume.
        mSupervisor.scheduleResumeTopActivities();
    }
}

调用了 obtainStarter 方法:

// services/core/java/com/android/server/wm/ActivityStartController.java
ActivityStarter obtainStarter(Intent intent, String reason) {
    return mFactory.obtain().setIntent(intent).setReason(reason);
}

这里 mFactory.obtain() 是通过 ActivityStarter 的内部类 DefaultFactory 的 obtain() 方法来获取 ActivityStarter 实例的。并将传入的 intent 以及 reason 设置进去,最后返回该 ActivityStarter 对象。 然后在 startHomeActivity 方法中再对该对象进行一些相关赋值,最后执行 ActivityStarter 的 execute 方法。

ActivityStarter 是加载 Activity 的控制类,会收集所有的逻辑来决定如何将 Intent 和 Flags 转换为 Activity,并将 Activity 和 Task 以及 Stack 相关联。它在调用 ActivityStarter 的 execute 方法之前一直有效。

根据前面提供的请求参数解析必要的信息,执行启动 Activity 的请求,启动了 Launcher 进程,最终进入到 Launcher 的 onCreate 方法中。

注:Activity 的详细启动流程不在这里过多介绍。(后期会补充链接)

Launcher 的 AndroidManifest.xml:

...
<activity
    android:name="com.android.launcher3.Launcher"
    android:launchMode="singleTask"
    android:clearTaskOnLaunch="true"
    android:stateNotNeeded="true"
    android:windowSoftInputMode="adjustPan"
    android:screenOrientation="unspecified"
    android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density"
    android:resizeableActivity="true"
    android:resumeWhilePausing="true"
    android:taskAffinity=""
    android:exported="true"
    android:enabled="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.HOME" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.MONKEY"/>
        <category android:name="android.intent.category.LAUNCHER_APP" />
    </intent-filter>
    <meta-data
        android:name="com.android.launcher3.grid.control"
        android:value="${packageName}.grid_control" />
</activity>
...

通过以下代码可以跳到 LauncherActivity,即 Launcher 的主界面:

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);

3. Launcher 中应用图标的显示过程

Launcher 的 onCreate 方法:

// packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    // 获取 LauncherAppState 实例
    LauncherAppState app = LauncherAppState.getInstance(this);
    // 获取 LauncherModel 实例
    mModel = app.getModel();

    ...
    if (!mModel.addCallbacksAndLoad(this)) {
        if (!internalStateHandled) {
            // If we are not binding synchronously, pause drawing until initial bind complete,
            // so that the system could continue to show the device loading prompt
            mOnInitialBindListener = Boolean.FALSE::booleanValue;
        }
    }

    ...
}

LauncherAppState 是一个单例模式,获取 LauncherAppState 对象 app。然后通过 LauncherAppState 的 getModel 方法获取到一个 LauncherModel 对象 mModel。

mModel 在 LauncherAppState 的构造函数中进行了初始化:

// src/com/android/launcher3/LauncherAppState.java
public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
    mContext = context;

    mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
    mIconProvider = new LauncherIconProvider(context);
    mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
            iconCacheFileName, mIconProvider);
    mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
            iconCacheFileName != null);
    mOnTerminateCallback.add(mIconCache::close);
    mOnTerminateCallback.add(mModel::destroy);
}

LauncherModel 保存着 Launcher 在内存中的各种状态。LauncherModel 的构造函数:

// src/com/android/launcher3/LauncherModel.java
LauncherModel(@NonNull final Context context, @NonNull final LauncherAppState app,
        @NonNull final IconCache iconCache, @NonNull final AppFilter appFilter,
        final boolean isPrimaryInstance) {
    mApp = app;
    mModelDbController = new ModelDbController(context);
    mBgAllAppsList = new AllAppsList(iconCache, appFilter);
    mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
            isPrimaryInstance);
}

回到 Launcher onCreate 方法中,接着调用 LauncherModel 的 addCallbacksAndLoad 方法,传入 this,添加回调监听 mModel 的更新:

// src/com/android/launcher3/LauncherModel.java
public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
    synchronized (mLock) {
        addCallbacks(callbacks);
        return startLoader(new Callbacks[] { callbacks });

    }
}

public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
    synchronized (mLock) {
        addCallbacks(callbacks);
        return startLoader(new Callbacks[] { callbacks });

    }
}

添加完回调之后,进入 startLoader 进行 app 信息查询:

// src/com/android/launcher3/LauncherModel.java
private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
    ...
    synchronized (mLock) {
        ...

        if (callbacksList.length > 0) {
            ...
            } else {
                ...
                mLoaderTask = new LoaderTask(
                        mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, launcherBinder);
                MODEL_EXECUTOR.post(mLoaderTask);
            }
        }
    }
    return false;
}

先总体概括一下,此部分创建了一个 HandlerThread,用于执行与 Launcher 内容相关的内容,比如执行加载 icons 任务等。下面进行进一步解析。

创建一个 LoaderTask 对象,LoaderTask 可运行用于加载与 Launcher 相关内容的线程。MODEL_EXECUTOR 为运行 Launcher Model 相关任务的执行器,如加载 icons、更新数据库。MODEL_EXECUTOR 为一个 LooperExecutor 对象:

// Launcher3/src/com/android/launcher3/util/Executors.java
// 创建 Handler,传入 HandlerThread 的 Looper。handler 作用是向 HandlerThread 发送消息。
public static final LooperExecutor MODEL_EXECUTOR =
        new LooperExecutor(createAndStartNewLooper("launcher-loader"));
        
public static Looper createAndStartNewLooper(String name) {
    return createAndStartNewLooper(name, Process.THREAD_PRIORITY_DEFAULT);
}

createAndStartNewLooper 创建了具有消息循环的线程 HandlerThread 对象。

// 创建了具有消息循环的线程 HandlerThread 对象。
public static Looper createAndStartNewLooper(String name, int priority) {
    HandlerThread thread = new HandlerThread(name, priority);
    thread.start();
    return thread.getLooper();
}

返回 HandlerThread 的 Looper,并创建了 LooperExecutor 对象 MODEL_EXECUTOR,MODEL_EXECUTOR 中创建了该 Looper 的 Handler 对象:

// Launcher3/src/com/android/launcher3/util/LooperExecutor.java
public LooperExecutor(Looper looper) {
    mHandler = new Handler(looper);
}

另外要注意一下,在创建 LoaderTask 时候,获取了系统服务 launcherapps,对应的类是 android.content.pm 中的 LauncherApps:

LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
        ModelDelegate modelDelegate, @NonNull LauncherBinder launcherBinder,
        UserManagerState userManagerState) {
    mApp = app;
    ...
    // public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
    mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
    ...
}

回到 startLoader 方法中,接着执行了 MODEL_EXECUTOR 的 post 方法,并传入了创建的 mLoaderTask:

public void post(Runnable runnable) {
    getHandler().post(runnable);
}

此方法中调用了 mHandler 的 post 方法,传入的 LoaderTask 实现了 Runnable 接口,当 LoaderTask 所描述的消息被处理时,则会调用他的 run 方法:

// Launcher3/src/com/android/launcher3/model/LoaderTask.java
public class LoaderTask implements Runnable {
    ...
    public void run() {
        ...
        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            List<ShortcutInfo> allShortcuts = new ArrayList<>();
            // 加载工作区信息
            loadWorkspace(allShortcuts, "", memoryLogger);
            ...
            // 绑定工作区
            mLauncherBinder.bindWorkspace(true /* incrementBindId */, /* isBindSync= */ false);
            ...
            // second step
            Trace.beginSection("LoadAllApps");
            List<LauncherActivityInfo> allActivityList;
            try {
            // 加载系统已经安装的应用程序信息,通过 PackageManagerService 查询
               allActivityList = loadAllApps();
            } 
            ...
            mLauncherBinder.bindAllApps();
            ...
        } 
        ...
    }
}

loadWorkspace 方法用来加载工作区信息,bindWorkspace 方法用来绑定工作区。loadAllApps 方法用来加载系统已经安装的应用程序信息,重点看一下这个方法:

// src/com/android/launcher3/model/LoaderTask.java
private List<LauncherActivityInfo> loadAllApps() {
    final List<UserHandle> profiles = mUserCache.getUserProfiles();
    List<LauncherActivityInfo> allActivityList = new ArrayList<>();
    // Clear the list of apps
    mBgAllAppsList.clear();

    List<IconRequestInfo<AppInfo>> iconRequestInfos = new ArrayList<>();
    boolean isWorkProfileQuiet = false;
    boolean isPrivateProfileQuiet = false;
    for (UserHandle user : profiles) {
        // Query for the set of apps
        final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
        ...
        allActivityList.addAll(apps);
    }
    ...
    return allActivityList;
}

由上文可知 mLauncherApps 是系统服务,调用了 LauncherApps 的 getActivityList 方法:

// android/content/pm/LauncherApps.java
private final ILauncherApps mService;
public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
    logErrorForInvalidProfileAccess(user);
    try {
        return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),
                packageName, user), user);
    } catch (RemoteException re) {
        throw re.rethrowFromSystemServer();
    }
}

mService 是一个 ILauncherApps 对象,LauncherAppsService 的内部类 LauncherAppsImpl 实现了该接口。

注:LauncherAppsService 是在 SystemServer 启动过程中的 startOtherServices 方法中启动,具体启动过程参考:Android 系统启动过程(三) —— SystemServer 进程

通过 IPC 调用 LauncherAppsService 的内部类 LauncherAppsImpl 的 getLauncherActivities 方法。

public ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities(
        String callingPackage, @Nullable String packageName, UserHandle user)
        throws RemoteException {
    ParceledListSlice<LauncherActivityInfoInternal> launcherActivities =
            queryActivitiesForUser(
                    callingPackage,
                    new Intent(Intent.ACTION_MAIN)
                            .addCategory(Intent.CATEGORY_LAUNCHER)
                            .setPackage(packageName),
                    user);
    ...
}

传入 Intent.ACTION_MAINIntent.CATEGORY_LAUNCHER 查询所有 LauncherActivityInfo,不难看出,Intent.ACTION_MAINIntent.CATEGORY_LAUNCHER 正是对应着每一个 App 开启的第一个 Activity 的配置信息。最终是通过 PackageManagerService 查询到对应信息,此处不进行深入分析。

接着调用 bindAllApps 对 app 数据进行绑定,进行相应的 UI 刷新等。

4. Launcher 图标点击事件

下面介绍一下点击应用图标响应事件。首先看一下所有应用的网格视图适配器 AllAppsGridAdapter,看一下它的 onCreateViewHolder 方法,是继承自父类 BaseAllAppsAdapter 的 onCreateViewHolder 方法:

// src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case VIEW_TYPE_ICON:
            int layout = !FeatureFlags.enableTwolineAllapps() ? R.layout.all_apps_icon
                            : R.layout.all_apps_icon_twoline;
            BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                    layout, parent, false);
            icon.setLongPressTimeoutFactor(1f);
            icon.setOnFocusChangeListener(mIconFocusListener);
            icon.setOnClickListener(mOnIconClickListener);
            icon.setOnLongClickListener(mOnIconLongClickListener);
            // Ensure the all apps icon height matches the workspace icons in portrait mode.
            icon.getLayoutParams().height =
                    mActivityContext.getDeviceProfile().allAppsCellHeightPx;
            return new ViewHolder(icon);
        ...
    }
}

点击 icon 时设置了点击事件,添加了 mOnIconClickListener。在 BaseAllAppsAdapter 构造函数中对 mOnIconClickListener 进行赋值:

public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
        AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider,
        PrivateSpaceHeaderViewController privateSpaceHeaderViewController) {
    mActivityContext = activityContext;
    mApps = apps;
    mLayoutInflater = inflater;

    mOnIconClickListener = mActivityContext.getItemOnClickListener();
    mOnIconLongClickListener = mActivityContext.getAllAppsItemLongClickListener();

    mAdapterProvider = adapterProvider;
    mPrivateSpaceHeaderViewController = privateSpaceHeaderViewController;
}

getItemOnClickListener 方法实现在 BaseDraggingActivity 中:

// src/com/android/launcher3/BaseDraggingActivity.java
@Override
public View.OnClickListener getItemOnClickListener() {
    return ItemClickHandler.INSTANCE;
}

该方法返回了 ItemClickHandler 的实例:

// src/com/android/launcher3/touch/ItemClickHandler.java
public static final OnClickListener INSTANCE = ItemClickHandler::onClick;

private static void onClick(View v) {
    ...
    Object tag = v.getTag();
    if (tag instanceof WorkspaceItemInfo) {
        ...
    } else if (tag instanceof FolderInfo) {
        ...
    } else if (tag instanceof AppInfo) {
        startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
    } else if 
    ...
}

在 onClick 方法中根据 View 的类型进行判断接下来如何执行,View 的 Tag 是在 BaseAllAppsAdapter 的 onBindViewHolder 方法中设置的:

// src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
public void onBindViewHolder(ViewHolder holder, int position) {
    switch (holder.getItemViewType()) {
        case VIEW_TYPE_ICON: {
            AdapterItem adapterItem = mApps.getAdapterItems().get(position);
            BubbleTextView icon = (BubbleTextView) holder.itemView;
            icon.reset();
            icon.applyFromApplicationInfo(adapterItem.itemInfo);
            icon.setOnFocusChangeListener(mIconFocusListener);
            break;
        }
        case VIEW_TYPE_EMPTY_SEARCH: {
            ...
    }
}

在 BubbleTextView 的 applyFromApplicationInfo 中:

public void applyFromApplicationInfo(AppInfo info) {
    ...
    setItemInfo(info);
    ...
}

向 setItemInfo 方法传入了 AppInfo:

public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {

    public static final AppInfo[] EMPTY_ARRAY = new AppInfo[0];
    public static final Comparator<AppInfo> COMPONENT_KEY_COMPARATOR = (a, b) -> {
        int uc = a.user.hashCode() - b.user.hashCode();
        return uc != 0 ? uc : a.componentName.compareTo(b.componentName);
    };
    
    public Intent intent;

    @NonNull
    public ComponentName componentName;

    // Section name used for indexing.
    public String sectionName = "";

    /**
     * The uid of the application.
     * The kernel user-ID that has been assigned to this application. Currently this is not a unique
     * ID (multiple applications can have the same uid).
     */
    public int uid = -1;
    ...
}

AppInfo 存储了一个 App 的一些信息,比如点击跳转所需的 Intent。

在 applyFromApplicationInfo 的 setItemInfo 方法中传入了 AppInfo,看一下 setItemInfo 方法:

// src/com/android/launcher3/BubbleTextView.java
protected void setItemInfo(ItemInfoWithIcon itemInfo) {
    setTag(itemInfo);
}

调用了 View 的 setTag 方法,将 itemInfo 设置了进去。所以此处设置 Tag 为 AppInfo 类型。

回到 ItemClickHandler 的 onCreate 方法中,我们就知道了 getTag 的值为 AppInfo 类型。所以点击 icon 后,会执行 startAppShortcutOrInfoActivity 方法,在此方法中又调用了 Launcher 的 startActivitySafely,这时开始了应用程序开启流程:

// src/com/android/launcher3/touch/ItemClickHandler.java
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
    ...
    launcher.startActivitySafely(v, intent, item);
}

至此 Launcher 启动过程介绍完毕。