在Android启动过程-万字长文(Android14)中介绍了Android系统的启动过程,本篇文章将继续介绍桌面应用Launcher。
一、Launcher介绍
- 在Android启动过程-万字长文(Android14)中提到Launcher是Android系统启动后,由SystemServer
Activity Manager Service (AMS)加载的第一个应用程序 - Launcher又被称为桌面程序,负责Android桌面的启动和管理
- 用户使用的应用程序(App)都是通过Launcher来启动的
二、下载及编译
2.1 下载
- 使用Git下载Launcher源码:
git clone https://android.googlesource.com/platform/packages/apps/Launcher3
- 进入项目目录
cd Launcher3
- 切换到Android14分支
git checkout android14-release
2.2 编译
使用AndroidStudio编译下载好的Launcher3工程
编译过程中遇到问题及解决方案可以参考以下博客:
三、源码解析
3.1 AndroidManifest.xml
在项目根目录的AndroidManifest.xml,定义了Launcher做为桌面程序的属性:
<application> <activity android:name="com.android.launcher3.Launcher" android:launchMode="singleTask"> <intent-filter> <category android:name="android.intent.category.HOME" /> </intent-filter> </activity> </application>
- android.intent.category.HOME: 告诉系统这是一个启动器(Launcher)应用程序,系统在初始化完成后会通过
ActivityTaskManagerService的getHomeIntent方法获取和启动桌面程序。具体可参见Android启动过程-万字长文(Android14) - 开发人员也可以自己开发一个桌面程序(如微软桌面),用户安装完成后,可以在系统设置中修改默认启动的桌面程序
3.2 Launcher.java
Launcher.java是Launcher的启动页面,负责资源初始化和桌面UI创建
3.2.1 onCreate方法
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取 LauncherAppState 实例和模型 LauncherAppState app = LauncherAppState.getInstance(this); mModel = app.getModel(); // 初始化不变的设备配置文件 InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); initDeviceProfile(idp); idp.addOnChangeListener(this); // 获取共享首选项和图标缓存 mSharedPrefs = LauncherPrefs.getPrefs(this); mIconCache = app.getIconCache(); // 创建无障碍代理 mAccessibilityDelegate = createAccessibilityDelegate(); // 初始化拖动控制器 initDragController(); // 创建所有应用程序控制器 mAllAppsController = new AllAppsTransitionController(this); // 创建状态管理器 mStateManager = new StateManager<>(this, NORMAL); // 创建引导首选项 mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs); // 设置视图 setupViews(); // 初始化Widget mAppWidgetManager = new WidgetManagerHelper(this); mAppWidgetHolder = createAppWidgetHolder(); mAppWidgetHolder.startListening(); // 设置内容视图 setContentView(getRootView()); ComposeInitializer.initCompose(this); }
3.2.2 setupViews方法
protected void setupViews() { // 创建根视图 inflateRootView(R.layout.launcher); // 获取拖动层和焦点处理器 mDragLayer = findViewById(R.id.drag_layer); mFocusHandler = mDragLayer.getFocusIndicatorHelper(); // 获取工作区、总览面板和Hotseat mWorkspace = mDragLayer.findViewById(R.id.workspace); mWorkspace.initParentViews(mDragLayer); mOverviewPanel = findViewById(R.id.overview_panel); mHotseat = findViewById(R.id.hotseat); // 将工作区设置为Hotseat mHotseat.setWorkspace(mWorkspace); // 设置拖动层 mDragLayer.setup(mDragController, mWorkspace); // 设置工作区 mWorkspace.setup(mDragController); // 在工作区绑定之前,确保我们将壁纸偏移锁定到默认状态,否则在RTL中我们将更新错误的偏移量 mWorkspace.lockWallpaperToDefaultPage(); mWorkspace.bindAndInitFirstWorkspaceScreen(); mDragController.addDragListener(mWorkspace); // 获取搜索/删除/卸载栏 mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar); // 设置应用程序视图 mAppsView = findViewById(R.id.apps_view); mAppsView.setAllAppsTransitionController(mAllAppsController); // 设置拖动控制器(拖动目标必须按优先级的相反顺序添加) mDropTargetBar.setup(mDragController); mAllAppsController.setupViews(mScrimView, mAppsView); // 如果启用了点分页,则设置工作区的分页指示器 if (SHOW_DOT_PAGINATION.get()) { mWorkspace.getPageIndicator().setShouldAutoHide(true); mWorkspace.getPageIndicator().setPaintColor( Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText) ? Color.BLACK : Color.WHITE); } }
- Workspace:工作区,也是我们常说的桌面区域,包括搜索框,桌面,壁纸
- AppsView:应用程序列表
- Widget:小组件
三、Workspace、AppsView和Widget示例
3.1 Workspace(工作区)

- 结构说明

3.2 AppsView(应用程序视图)

3.3 Widget(小组件)

四、点击App图标的事件响应
4.1 触发ItemClickHandler的onClick方法
- ItemClickHandler负责处理桌面应用图标的点击事件。
- 桌面图标的点击事件最终会触发ItemClickHandler的onClick方法
- onClick方法最终会触发startAppShortcutOrInfoActivity方法
/** * Class for handling clicks on workspace and all-apps items */ public class ItemClickHandler { private static void onClick(View v) { startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher); } // 通知launcher启动Activity private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) { launcher.startActivitySafely(v, intent, item); } }
4.2 Launcher通知系统启动App
在Launcher.java的startActivitySafely方法中调用ActivityContext.java的startActivitySafely方法
public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) { RunnableList result = super.startActivitySafely(v, intent, item); }
在ActivityContext.java的startActivitySafely方法中调用了
public interface ActivityContext { default RunnableList startActivitySafely( View v, Intent intent, @Nullable ItemInfo item) { if (isShortcut) { // Shortcuts need some special checks due to legacy reasons. startShortcutIntentSafely(intent, optsBundle, item); } } default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) { if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { // 通过快捷方式启动 startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user); } else { // 普通方式启动,应用程序走这个分支 ((Context) this).startActivity(intent, optsBundle); } } }
最终通过frameworks/base/core/java/android/app/Activity.java 源码地址中的startActivity方法启动了对应的应用程序。