在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工程
編譯過程中遇到問題及解決方案可以參考以下部落格:
- 基於android 11的Launcher3本地Android studio調通流程
- AndroidStudio編譯除錯aosp11R 的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);
}
}
}
Android 應用快捷方式(Shortcut)官方文件
最終透過frameworks/base/core/java/android/app/Activity.java 原始碼地址中的startActivity
方法啟動了對應的應用程式。