Android 13 Launcher 基礎(chǔ)認(rèn)識(一)

學(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)巫玻。
UI組件.png
桌面組件.png
三丛忆、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ù)加載拯钻。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載帖努,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末粪般,一起剝皮案震驚了整個濱河市拼余,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亩歹,老刑警劉巖匙监,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異小作,居然都是意外死亡亭姥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門顾稀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來达罗,“玉大人,你說我怎么就攤上這事静秆×溉啵” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵抚笔,是天一觀的道長扶认。 經(jīng)常有香客問我,道長殊橙,這世上最難降的妖魔是什么辐宾? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蛀柴,結(jié)果婚禮上螃概,老公的妹妹穿的比我還像新娘。我一直安慰自己鸽疾,他們只是感情好吊洼,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著制肮,像睡著了一般冒窍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上豺鼻,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天综液,我揣著相機與錄音,去河邊找鬼儒飒。 笑死谬莹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播附帽,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼埠戳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蕉扮?” 一聲冷哼從身側(cè)響起整胃,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喳钟,沒想到半個月后屁使,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡奔则,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年蛮寂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片应狱。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡共郭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疾呻,到底是詐尸還是另有隱情除嘹,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布岸蜗,位于F島的核電站尉咕,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏璃岳。R本人自食惡果不足惜年缎,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铃慷。 院中可真熱鬧单芜,春花似錦、人聲如沸犁柜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馋缅。三九已至扒腕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間萤悴,已是汗流浹背瘾腰。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留覆履,地道東北人蹋盆。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓费薄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親栖雾。 傳聞我的和親對象是個殘疾皇子义锥,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容