在Android啟動過程-萬字長文(Android14)中介紹了Android系統的啟動過程,本篇文章將繼續介紹桌面應用Launcher。 一、Launcher介紹 在Android啟動過程-萬字長文(Android14)中提到Launcher是Android系統啟動後,由SystemServerA ...
在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
方法啟動了對應的應用程式。