【 Android 10 系统启动 】系列 -- Launcher(应用门户)

鸦青矩阵
• 阅读 4459

前言

由于源码分析的代码量比较大,大部分博客网站的内容显示页面都比较窄,显示出来的效果都异常丑陋,所以您也可以直接查看 《 Thinking in Android 》 来阅读这边文章,希望这篇文章能帮你梳理清楚 “Launcher 启动流程”


核心源码

关键类 路径
SystemServer.java frameworks/base/services/java/com/android/server/SystemServer.java
ActivityManagerService.java frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
ActivityStack.java frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
ActivityTaskManagerService.java frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
RootActivityContainer.java frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java
Launcher.java packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
LauncherModel.java packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java
LauncherAppState.java packages/apps/Launcher3/src/com/android/launcher3/LauncherAppState.java
LoaderTask.java packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java


一、Launcher 启动流程

Android 系统启动的最后一步是启动一个 Home 应用程序,这个应用程序用来显示系统中已经安装的应用程序,我们称呼这个应用程序为 Launcher

应用程序 Launcher 在启动过程中会请求 PackageManagerService 返回系统中已经安装的应用程序的信息,并将这些信息封装成一个 快捷图标 列表显示在系统屏幕上,这样用户可以通过点击这些快捷图标来启动相应的应用程序了。

1.1 SystemServer.startOtherServices()

// frameworks/base/services/java/com/android/server/SystemServer.java

public final class SystemServer {

    private ActivityManagerService mActivityManagerService;

    private void startOtherServices() {
        ... ...

        mActivityManagerService.systemReady(() -> {    // 调用 ActivityManagerService 的 systemReady() 方法
            Slog.i(TAG, "Making services ready");
            ... ...

        }
        ... ...

    }

}

startOtherServices() 方法中,会调用 ActivityManagerServicesystemReady() 方法。

1.2 ActivityManagerService.systemReady()

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

    @VisibleForTesting
    public ActivityTaskManagerInternal mAtmInternal;

    public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
        ... ...

        synchronized (this) {
            ... ...

            // 调用 resumeTopActivities() 方法,这是一个抽象方法
            mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
            mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
            ... ...

        }
        ... ...

    }

}    

1.3 ActivityTaskManagerService.resumeTopActivities()

resumeTopActivities() 方法由 ActivityTaskManagerService 的内部类 LocalService 实现。

// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {

    RootActivityContainer mRootActivityContainer;

    final class LocalService extends ActivityTaskManagerInternal {
        ... ...

        @Override
        public void resumeTopActivities(boolean scheduleIdle) {
            synchronized (mGlobalLock) {
                // 调用 RootActivityContainer 的 resumeFocusedStacksTopActivities() 方法
                mRootActivityContainer.resumeFocusedStacksTopActivities();
                if (scheduleIdle) {
                    mStackSupervisor.scheduleIdleLocked();
                }
            }
        }
        ... ...

    }
    
}

1.4 RootActivityContainer.resumeFocusedStacksTopActivities()

// frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java

class RootActivityContainer extends ConfigurationContainer
        implements DisplayManager.DisplayListener {

    boolean resumeFocusedStacksTopActivities() {
        return resumeFocusedStacksTopActivities(null, null, null);
    }

    boolean resumeFocusedStacksTopActivities(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
        ... ...

        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            boolean resumedOnDisplay = false;
            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
            ... ...

            if (!resumedOnDisplay) {
                final ActivityStack focusedStack = display.getFocusedStack();
                if (focusedStack != null) {
                    // 调用 resumeTopActivityUncheckedLocked() 方法
                    focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
                }
            }
        }
  
        return result;
    }

}

1.5 ActivityStack.resumeTopActivityUncheckedLocked()

// frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java

class ActivityStack extends ConfigurationContainer {

    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mInResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }
  
        boolean result = false;
        try {
            // Protect against recursion.
            mInResumeTopActivity = true;
            result = resumeTopActivityInnerLocked(prev, options);  // 调用 resumeTopActivityInnerLocked() 方法

            final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
            if (next == null || !next.canTurnScreenOn()) {
                checkReadyForSleep();
            }
        } finally {
            mInResumeTopActivity = false;
        }
  
        return result;
    }

}

1.6 ActivityStack.resumeTopActivityInnerLocked()

// frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

class ActivityStack extends ConfigurationContainer {

    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        ... ...

        if (!hasRunningActivity) {
            // There are no activities left in the stack, let's look somewhere else.
            return resumeNextFocusableActivityWhenStackIsEmpty(prev, options);
        }
        ... ...

    }

}

1.7 ActivityStack.resumeNextFocusableActivityWhenStackIsEmpty()

// frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

class ActivityStack extends ConfigurationContainer {

    protected final RootActivityContainer mRootActivityContainer;

    private boolean resumeNextFocusableActivityWhenStackIsEmpty(ActivityRecord prev,
            ActivityOptions options) {
        final String reason = "noMoreActivities";
  
        if (!isActivityTypeHome()) {
            final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
            if (nextFocusedStack != null) {
                return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack,
                        prev, null /* targetOptions */);
                }
            }
  
        // If the current stack is a home stack, or if focus didn't switch to a different stack -
        // just start up the Launcher...
        ActivityOptions.abort(options);
        if (DEBUG_STATES) Slog.d(TAG_STATES,
                "resumeNextFocusableActivityWhenStackIsEmpty: " + reason + ", go home");
        // 调用 resumeHomeActivity() 方法
        return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
    }

}

1.8 RootActivityContainer.resumeHomeActivity()

// frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java

class RootActivityContainer extends ConfigurationContainer
        implements DisplayManager.DisplayListener {

    boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
        if (!mService.isBooting() && !mService.isBooted()) {
            // Not ready yet!
            return false;
        }

        if (displayId == INVALID_DISPLAY) {
            displayId = DEFAULT_DISPLAY;
        }

        final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
        final String myReason = reason + " resumeHomeActivity";

        // Only resume home activity if isn't finishing.
        if (r != null && !r.finishing) {
            r.moveFocusableActivityToTop(myReason);
            return resumeFocusedStacksTopActivities(r.getActivityStack(), prev, null);
        }
        // 调用 startHomeOnDisplay() 方法
        return startHomeOnDisplay(mCurrentUser, myReason, displayId);
    }

}

1.9 RootActivityContainer.startHomeOnDisplay()

// frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java

class RootActivityContainer extends ConfigurationContainer
        implements DisplayManager.DisplayListener {

    ActivityTaskManagerService mService;

    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
        return startHomeOnDisplay(userId, reason, displayId, false /* allowInstrumenting */,
                false /* fromHomeKey */);
    }

    boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
            boolean fromHomeKey) {
        ... ...
  
        // Updates the home component of the intent.
        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
        ... ...

        // 启动 Launcher
        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
                displayId);
        return true;
    }

}


二、应用图标显示流程

接下来我们看看 Launcher 是如何加载并显示所有已安装 Apk 的:

2.1 Launcher.onCreate()

// packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

/**
 * Default launcher application.
 */
public class Launcher extends BaseDraggingActivity implements LauncherExterns,
       LauncherModel.Callbacks, LauncherProviderChangeListener, UserEventDelegate,
       InvariantDeviceProfile.OnIDPChangeListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ... ...

        LauncherAppState app = LauncherAppState.getInstance(this);
        ... ...

        mModel = app.setLauncher(this);    // 调用 setLauncher() 方法
        ... ...

        if (!mModel.startLoader(currentScreen)) {    // 见 2.4
            if (!internalStateHandled) {
                // If we are not binding synchronously, show a fade in animation when
                // the first page bind completes.
                mDragLayer.getAlphaProperty(ALPHA_INDEX_LAUNCHER_LOAD).setValue(0);
            }
        } else {
            // Pages bound synchronously.
            mWorkspace.setCurrentPage(currentScreen);

            setWorkspaceLoading(true);
        }
        ... ...
        
    }

}

2.2 LauncherAppState.setLauncher()

// packages/apps/Launcher3/src/com/android/launcher3/LauncherAppState.java

public class LauncherAppState {

    LauncherModel setLauncher(Launcher launcher) {
        getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
        mModel.initialize(launcher);    // 调用 initialize() 方法
        return mModel;
    }

}

2.3 LauncherModel.initialize()

// packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java

public class LauncherModel extends BroadcastReceiver
        implements LauncherAppsCompat.OnAppsChangedCallbackCompat {

    /**
     * Set this as the current Launcher activity object for the loader.
     */
    public void initialize(Callbacks callbacks) {
        synchronized (mLock) {
            Preconditions.assertUIThread();
            mCallbacks = new WeakReference<>(callbacks);
        }
    }

}

initialize 方法中会将 Callbacks(也就是传入的 Launcher)封装成一个 弱引用对象,因此我们得知 mCallbacks 变量指的就是封装成弱引用对象的 Launcher

重新回到 oncreate() 函数,有个 startLoader() 方法!

2.4 LauncherModel.startLoader()

// packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java

public class LauncherModel extends BroadcastReceiver
        implements LauncherAppsCompat.OnAppsChangedCallbackCompat {

    public boolean startLoader(int synchronousBindPage) {
        // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
        synchronized (mLock) {
            // Don't bother to start the thread if we know it's not going to do anything
            if (mCallbacks != null && mCallbacks.get() != null) {
                final Callbacks oldCallbacks = mCallbacks.get();
                // Clear any pending bind-runnables from the synchronized load process.
                mUiExecutor.execute(oldCallbacks::clearPendingBinds);

                // If there is already one running, tell it to stop.
                stopLoader();
                LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel,
                        mBgAllAppsList, synchronousBindPage, mCallbacks);
                if (mModelLoaded && !mIsLoaderTaskRunning) {
                    // Divide the set of loaded items into those that we are binding synchronously,
                    // and everything else that is to be bound normally (asynchronously).
                    loaderResults.bindWorkspace();
                    // For now, continue posting the binding of AllApps as there are other
                    // issues that arise from that.
                    loaderResults.bindAllApps();
                    loaderResults.bindDeepShortcuts();
                    loaderResults.bindWidgets();
                    return true;
                } else {
                    startLoaderForResults(loaderResults);    // 调用 startLoaderForResults() 方法
                }
            }
        }
        return false;
    }

}

2.5 LauncherModel.startLoaderForResults()

// packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java

public class LauncherModel extends BroadcastReceiver
        implements LauncherAppsCompat.OnAppsChangedCallbackCompat {

   @Thunk static final Handler sWorker = new Handler(mWorkerLooper);

    public void startLoaderForResults(LoaderResults results) {
        synchronized (mLock) {
            stopLoader();
            mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);

            // Always post the loader task, instead of running directly (even on same thread) so
            // that we exit any nested synchronized blocks
            sWorker.post(mLoaderTask);
        }
    }

}

2.6 LoaderTask.run()

LoaderTask 所描述的消息被处理时则会调用它的 run() 方法:

// packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java

public class LoaderTask implements Runnable {

    public void run() {
        synchronized (this) {
            // Skip fast if we are already stopped.
            if (mStopped) {
                return;
            }
        }

        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
            loadWorkspace();    // 加载工作区信息

            verifyNotStopped();
            TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
            mResults.bindWorkspace();
            ... ...

            // second step
            TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
            List<LauncherActivityInfo> allActivityList = loadAllApps();    // 加载系统已经安装的应用程序信息

            TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
            verifyNotStopped();
            mResults.bindAllApps();
            ... ...

            // third step
            TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts");
            loadDeepShortcuts();

            verifyNotStopped();
            TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts");
            mResults.bindDeepShortcuts();
            ... ...

            // fourth step
            TraceHelper.partitionSection(TAG, "step 4.1: loading widgets");
            List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);

            verifyNotStopped();
            TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets");
            mResults.bindWidgets();
            ... ...

            transaction.commit();
        } catch (CancellationException e) {
            // Loader stopped, ignore
            TraceHelper.partitionSection(TAG, "Cancelled");
        }
        TraceHelper.endSection(TAG);
    }

}

2.7 LoaderTask.loadAllApps()

// packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java

public class LoaderTask implements Runnable {

    private List<LauncherActivityInfo> loadAllApps() {
        final List<UserHandle> profiles = mUserManager.getUserProfiles();
        List<LauncherActivityInfo> allActivityList = new ArrayList<>();
        // Clear the list of apps
        mBgAllAppsList.clear();
        for (UserHandle user : profiles) {
            // Query for the set of apps
            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
            // Fail if we don't have any apps
            // TODO: Fix this. Only fail for the current user.
            if (apps == null || apps.isEmpty()) {
                return allActivityList;
            }
            boolean quietMode = mUserManager.isQuietModeEnabled(user);
            // Create the ApplicationInfos
            for (int i = 0; i < apps.size(); i++) {
                LauncherActivityInfo app = apps.get(i);
                // This builds the icon bitmaps.
                mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
            }
            allActivityList.addAll(apps);
        }

        if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
            // get all active sessions and add them to the all apps list
            for (PackageInstaller.SessionInfo info :
                    mPackageInstaller.getAllVerifiedSessions()) {
                mBgAllAppsList.addPromiseApp(mApp.getContext(),
                        PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info));
            }
        }

        mBgAllAppsList.added = new ArrayList<>();
        return allActivityList;
    }

}

自此,Launcher 的启动和应用加载的流程源码分析完毕。

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
Unity横屏
Android下发现Unity里面的Player设置,并不能完全有效,比如打开了自动旋转,启动的时候还是会横屏,修改XML添加以下代码<applicationandroid:icon"@drawable/ic\_launcher"                    android:label"@string/app\_name"
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
鸦青矩阵
鸦青矩阵
Lv1
此曲只应天上有,人间能得几回闻。
文章
2
粉丝
0
获赞
0