學(xué)習(xí)筆記:
Android 10.0 launcher 啟動流程
Android 13 Launcher 基礎(chǔ)認(rèn)識(一)
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ū)圖標(biāo) 诗力、小部件 凰浮、所有應(yīng)用程序圖標(biāo) 、應(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的話就把事件傳到目標(biāo)view悬荣,交有目標(biāo)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來標(biāo)識拖動的方式您单,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(標(biāo)識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被稱為嚴(yán)苛模式家浇,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實例,此類主要保存圖標(biāo)信息
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ù)加載拯钻。