學(xué)習(xí)筆記:
Android 10.0 launcher 啟動流程
Android 13 Launcher 基礎(chǔ)認識(一)
Android 13 Launcher 數(shù)據(jù)加載分析(二)
Android 13 Launcher3 數(shù)據(jù)庫及Workspace 的數(shù)據(jù)加載與綁定(三)
一、Launcher 簡介
Launcher 是 Android 系統(tǒng)不可缺少的部分,我們通常稱之為 Android 系統(tǒng)的桌面,它在 Android 系統(tǒng)中起著重要的作用。
- Launcher 是 Android 系統(tǒng)的啟動器。在 Launcher 中可以啟動你想要使用的應(yīng)用程序。
- Launcher 也是應(yīng)用程序的管理器。可用來對應(yīng)用程序進行基礎(chǔ)的管理,比如刪除或者展示應(yīng)用程序。
- Launcher 更重要的意義在于它是一個桌面。在 Android 的桌面上,你可以放置各種快捷方式、桌面小部件,也可以通過 Launcher 更換壁紙,使你的桌面更炫更便利更加個性化。
二、Launcher 結(jié)構(gòu)
在 Launcher 中主要有兩大組件:
- UI組件:桌面(Workspace)、應(yīng)用程序菜單(Allapps)、快捷啟動欄(Hotseat)、搜索和頁面指示條、快捷菜單。
- 桌面組件:應(yīng)用程序的快捷方式及相關(guān)視圖實現(xiàn)(DeepShortcuts)、文件夾及相關(guān)視圖實現(xiàn)、桌面小部件及相關(guān)組件(Widgets)。


三、launcher 啟動
參考Android系統(tǒng)開機到Launcher啟動流程分析、Android 10.0 launcher啟動流程。
四、主要文件和類
Launcher.java:launcher中主要的activity。
LoaderTask.java: 可運行用于加載啟動器內(nèi)容的線程: 工作區(qū)圖標 、小部件 、所有應(yīng)用程序圖標 、應(yīng)用程序快捷方式。
LauncherAppState.java:用于存儲全局變量,比如:緩存(各種cache),維護內(nèi)存數(shù)據(jù)的類(LauncherModel)。
DragLayer.java:launcher layout的rootview。DragLayer實際上也是一個抽象的界面,用來處理拖動和對事件進行初步處理然后按情況分發(fā)下去,角色是一個controller。它首先用onInterceptTouchEvent(MotionEvent)來攔截所有的touch事件,如果是長按item拖動的話不把事件傳下去,直接交由onTouchEvent()處理,這樣就可以實現(xiàn)item的移動了,如果不是拖動item的話就把事件傳到目標view,交有目標view的事件處理函數(shù)做相應(yīng)處理。如過有要對事件的特殊需求的話可以修改onInterceptTouchEvent(MotionEvent)來實現(xiàn)所需要的功能。
DragController.java:為Drag定義的一個接口。包含一個接口,兩個方法和兩個靜態(tài)常量。接口為DragListener(包含onDragStart(),onDragEnd()兩個函數(shù)),onDragStart()是在剛開始拖動的時候被調(diào)用,onDragEnd()是在拖動完成時被調(diào)用。在launcher中典型的應(yīng)用是DeleteZone,在長按拖動item時調(diào)用onDragStart()顯示,在拖動結(jié)束的時候onDragEnd()隱藏。兩個函數(shù)包括startDrag()和setDragItemInfo().startDrag()用于在拖動是傳遞要拖動的item的信息以及拖動的方式,setDragItemInfo()用于傳遞item的參數(shù)信息(包括位置以及大小)。兩個常量為DRAG_ACTION_MOVE,DRAG_ACTION_COPY來標識拖動的方式,DRAG_ACTION_MOVE為移動,表示在拖動的時候需要刪除原來的item,DRAG_ACTION_COPY為復(fù)制型的拖動,表示保留被拖動的item。
LauncherModel.java:輔助的文件。里面有許多封裝的對數(shù)據(jù)庫的操作。包含幾個線程,其中最主要的是ApplicationsLoader和DesktopItemsLoader。ApplicationsLoader在加載所有應(yīng)用程序時使用,DesktopItemsLoader在加載workspace的時候使用。其他的函數(shù)就是對數(shù)據(jù)庫的封裝,比如在刪除,替換,添加程序的時候做更新數(shù)據(jù)庫和UI的工作。
Workspace.java:抽象的桌面。由N個celllaout組成,從cellLayout更高一級的層面上對事件的處理。
LauncherProvider.java:launcher的數(shù)據(jù)庫,里面存儲了桌面的item的信息。在創(chuàng)建數(shù)據(jù)庫的時候會loadFavorites(db)方法,loadFavorites()會解析xml目錄下的default_workspace.xml文件,把其中的內(nèi)容讀出來寫到數(shù)據(jù)庫中,這樣就做到了桌面的預(yù)制。
CellLayout.java:組成workspace的view,繼承自viewgroup,既是一個dragSource,又是一個dropTarget,可以將它里面的item拖出去,也可以容納拖動過來的item。在workspace_screen里面定了一些它的view參數(shù)。
ItemInfo.java:對item的抽象,所有類型item的父類,item包含的屬性有id(標識item的id),cellX(在橫向位置上的位置,從0開始),cellY(在縱向位置上的位置,從0開始) ,spanX(在橫向位置上所占的單位格),spanY(在縱向位置上所占的單位格),screen(在workspace的第幾屏,從0開始),itemType(item的類型,有widget,search,application等),container(item所在的)。
LauncherSettings.java:字符串的定義。數(shù)據(jù)庫項的字符串定義,另外在這里定義了container的類型,還有itemType的定義,除此還有一些特殊的widget(如search,clock的定義等)的類型定義。
五、launcher 源碼分析
launcher.java 是 launcher 主要、第一個啟動的 activity,在其里面進行顯示、初始化一些 View。
launcher#onCreate()
// launcher.java
protected void onCreate(Bundle savedInstanceState) {
// 省略部分代碼......
Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
TraceHelper.FLAG_UI_EVENT);
if (DEBUG_STRICT_MODE) {
//StrictMode被稱為嚴苛模式,google提供用來進行測試的類
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
// 省略部分代碼......
super.onCreate(savedInstanceState);
// 單例模式,初始化LauncherAppState
LauncherAppState app = LauncherAppState.getInstance(this);
mOldConfig = new Configuration(getResources().getConfiguration());
// 獲取LauncherModel實例
mModel = app.getModel();
mRotationHelper = new RotationHelper(this);
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
// 初始化手機固件信息對象DeviceProfile
initDeviceProfile(idp);
idp.addOnChangeListener(this);
// 獲取sharedPreferences
mSharedPrefs = Utilities.getPrefs(this);
// 獲取IconCache實例,此類主要保存圖標信息
mIconCache = app.getIconCache();
mAccessibilityDelegate = createAccessibilityDelegate();
// 拖拽
mDragController = new LauncherDragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new StateManager<>(this, NORMAL);
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
// 獲取AppWidgetManager實例,用來管理widge
mAppWidgetManager = new WidgetManagerHelper(this);
mAppWidgetHost = createAppWidgetHost();
mAppWidgetHost.startListening();
// 設(shè)置布局
inflateRootView(R.layout.launcher);
// 初始化View,進行各種View的初始化事件綁定
setupViews();
crossFadeWithPreviousAppearance();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
// 對于狀態(tài)進行判斷,我們不需要進行任何的配置
if (internalStateHandled) {
if (savedInstanceState != null) {
// InternalStateHandler has already set the appropriate state.
// We dont need to do anything.
savedInstanceState.remove(RUNTIME_STATE);
}
}
restoreState(savedInstanceState);
mStateManager.reapplyState();
if (savedInstanceState != null) {
int[] pageIds = savedInstanceState.getIntArray(RUNTIME_STATE_CURRENT_SCREEN_IDS);
if (pageIds != null) {
mPagesToBindSynchronously = IntSet.wrap(pageIds);
}
}
// 加載、綁定數(shù)據(jù)(這里與之前版本的 startLoader() 作用一樣)
// 如果沒有綁定則進行加載、綁定數(shù)據(jù)
if (!mModel.addCallbacksAndLoad(this)) {
if (!internalStateHandled) {
Log.d(BAD_STATE, "Launcher onCreate not binding sync, prevent drawing");
// 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;
}
}
// For handling default keys
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
setContentView(getRootView());
if (mOnInitialBindListener != null) {
//getRootView().getViewTreeObserver().addOnPreDrawListener(mOnInitialBindListener);
}
getRootView().dispatchInsets();
// 注冊屏幕關(guān)閉廣播
registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
mOverlayManager = getDefaultOverlay();
PluginManagerWrapper.INSTANCE.get(this).addPluginListener(this,
LauncherOverlayPlugin.class, false /* allowedMultiple */);
mRotationHelper.initialize();
TraceHelper.INSTANCE.endSection(traceToken);
mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener(
() -> getStateManager().goToState(NORMAL));
if (Utilities.ATLEAST_R) {
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
setTitle(R.string.home_screen);
}
從代碼中可以看出,首先調(diào)用了 LauncherAppState.getInstance(this) 來初始化一個單例對象 ,而LauncherAppState 里面保存了一些比較常用的對象,方便其他地方通過單例來獲取,比如 IconCache、LauncherModel 等;并且注冊了廣播監(jiān)聽器和 ContentObserver ;設(shè)置布局 launcher.xml,調(diào)用 setupViews() 又是一些 View 的初始化,設(shè)置回調(diào)監(jiān)聽等。
總結(jié)一下 onCreate() 的工作:初始化對象、加載布局、注冊一些事件監(jiān)聽、以及開啟數(shù)據(jù)加載。