1. Launcher 简述
系统启动最后一步是启动 Launcher 来显示系统中已经安装的应用程序,Launcher 就是我们所说的桌面,自身也是一个应用程序。Launcher 在启动过程中会请求 PackageManagerService 返回系统中已经安装的应用程序信息,并将这些信息封装成一个快捷图标列表显示在系统屏幕上,这样用户可以通过点击这些快捷图标 icon 来启动相应的应用程序。
Launcher 是开机显示的桌面:
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_MAIN 和 Intent.CATEGORY_LAUNCHER 查询所有 LauncherActivityInfo,不难看出,Intent.ACTION_MAIN 和 Intent.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 启动过程介绍完毕。