1. 程式人生 > >Launcher的相關知識

Launcher的相關知識

Launcher是什麼

首先來說說Launcher是個什麼東西,我想剛接觸到這個東西的時候大家都是一頭霧水,然後會自然而然的問一個問題,總是聽到有人說Launcher,那Launcher是個什麼東西呢?其實Launcher就是一個Activity,Launcher的原始碼中也是繼承的Activity。體現在直觀方面就是手機的桌面,當我們開啟手機的時候,手機的桌面就是Launcher,一個Activity,只是這個Activity做的事情比較多,它在View方面可以左右滑動,可以響應長按等操作;在邏輯方面,它可以承載手機中所有應用的快捷方式,起著一個程式入口的作用。簡單說,Launcher就是一個複雜的Activity。

怎麼呼叫Launcher

既然Launcher是一個Activity,是一開機由系統呼叫的,那麼系統是怎麼識別這個特殊的Activity的呢?是由清單檔案的配置來識別的,如下:

<intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

你可以自己寫一個帶有這樣過濾器的Activity,在模擬器上執行之後,點選home鍵之後,系統會自動提示你進入那個Launcher。第三方的ROM我測試了幾款都不可以,這也可以想通。由於公司的Launcher是直接將Launcher原始碼拷貝過來,在這上面做的改動,所以這裡只是說系統的Launcher的載入過程。

關於Launcher的相關類

我想只要是開發過的人,當第一次看一個Activity的時候首先要看的就是這個Activity的佈局檔案了,由於工作中研究的大都是Launcher的載入邏輯,對佈局方面涉及不多,所以這裡就只羅列出Launcher中相關的類和對應的作用。

佈局方面:

   Draglayer:用於處理拖拽事件,當拖動桌面的一個View的時候,這個View就放到了DragLayer
   
    Workspace:手機螢幕上左右滑的螢幕,這個View表示可以滑動的單獨一屏
    
    PagedView:Workspace的父類,表示整個可以滑動的每一塊螢幕
    
    PageIndicator:表示桌面下方表示有幾個頁面的固定不動的小圓點
    
    Hotseat:表示桌面下方不隨這螢幕滑動而改變的一行
    
    SearchDropTargetBar:表示螢幕上方的搜尋框,在長按圖示的時候會變成刪除圖示

常用類

    LauncherModel :儲存桌面執行時的狀態資訊,它中的內部類LoaderTask完成了在桌面啟動時讀取資料庫資料,並把圖示和小工具新增到桌面
    
    BubblTextView:左面上的圖示都是基於這個類
    
    DragController:拖拽的控制類,具體的拖拽處理都放到這個類中
    
    LauncherAppState:在專案啟動的時候,這個類初始化一些必要的物件,如註冊廣播等    
      
    ItemInfo:儲存每一個應用的資訊類

Launcher的載入流程

找到onCreate()方法,開始一步一步的走流程。首先是這樣的一段程式碼

if (DEBUG_STRICT_MODE) {
            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());
        }

雖然在程式碼中你會看到標記DEBUG_STRICT_MODE已經置為了false,不會走下面的程式碼,但是作為一個有責任的程式設計師當然要一探究竟,經查閱,StricMode這個類是提供給開發這測試用的一個類,它包含可多種不同的策略,其中還有不同的規則,主要作用就是用來優化程式碼,對於提供的不同優化策略,有關於磁碟讀和寫的,有網路的,有記憶體的,開發者可以自己控制開關,結果會通過log的形式打印出來。值得注意的是,這個類只是在測試階段來使用,當專案上線的時候就可以關閉這個功能。 接著往下走:

LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();

這裡主要是為了LauncherAPPState這個物件,這個物件的作用,在上邊也提到過,主要是在Launcher啟動的時候初始化一些必要的物件,如註冊廣播啊,初始化一些內容提供者啊等等。

  Point smallestSize = new Point();
        Point largestSize = new Point();
        Point realSize = new Point();
        Display display = getWindowManager().getDefaultDisplay();
        display.getCurrentSizeRange(smallestSize, largestSize);
        display.getRealSize(realSize);
        DisplayMetrics dm = new DisplayMetrics();
        display.getMetrics(dm);

這段程式碼不用說也都看的懂,因為系統之後一套Launcher邏輯,而Android手機的尺寸是很多的,所以要提前獲取到螢幕的尺寸,這段程式碼就是用來獲取螢幕的尺寸的。

        // Lazy-initialize the dynamic grid
        //生成動態網格,儲存到DeviceProfile中
        DeviceProfile grid = app.initDynamicGrid(this,
        Math.min(smallestSize.x, smallestSize.y),
        Math.min(largestSize.x, largestSize.y),
        realSize.x, realSize.y,
        dm.widthPixels, dm.heightPixels);

當我們長按螢幕拖動螢幕上的圖示的時候,每一個圖片都有屬於自己的一個方格的位置這段程式碼就是在Launcher上來生成那樣的網格

//初始化LauncherModel:這個類和資料有關,儲存了桌面執行時的狀態資訊。當桌面啟動,讀取應用圖示和小工具,使用的就是它當中的內部類LoaderTask
LauncherModel mModel = app.setLauncher(this);

這段程式碼比較重要,首先我們要知道LauncherModel這個類的作用,他是用來儲存桌面執行時的狀態資訊,它中的內部類LoaderTask完成了在桌面啟動時讀取資料庫資料,並把圖示和小工具新增到桌面。因為LauncherModel這個類大部分都是耗時的操作,所以要拿到它操作完之後的資料就要使用回撥的方式,這裡把this傳遞過去,就是為了回撥使用。LauncherModel用來載入資料,Launcher用來顯示資料。

      //初始化IconCache,用於快取應用程式的圖示
       IconCache mIconCache = app.getIconCache();
        mIconCache.flushInvalidIcons(grid);//清空快取

       //建立拖拽控制物件
        mDragController = new DragController(this);
        mInflater = getLayoutInflater();

        mStats = new Stats(this);

        //初始化桌面外掛管理物件,它是AppWidgetService的代理
        mAppWidgetManager = AppWidgetManager.getInstance(this);

        //桌面外掛宿主。每一個桌面外掛要顯示到桌面上都必須有一個對應的桌面宿主
        mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
        mAppWidgetHost.startListening();

為了有更好的體驗,Launcher上載入的所有應用的圖示都是先提前在Launcher中快取起來的,使用的就是IconCache這個物件。然後建立拖拽物件,控制被使用者拖拽的應用圖示,同時建立桌面小部件的管理物件,開啟桌面小部件的監聽。

//檢查本地配置資訊是否發生變化,如果變化了要更新桌面對應的元素
        checkForLocaleChange();
        setContentView(R.layout.launcher);

        //初始化桌面上所有的佈局
        setupViews();
        //載入桌布
        setWallpaper();
        grid.layout(this);
        registerContentObservers();

        //系統預留方法,空方法
        lockAllApps();
        //儲存狀態
        mSavedState = savedInstanceState;
        //恢復狀態
        restoreState(mSavedState);

檢查本地配置資訊,是檢查如當時的時區,時間,經緯度等這些實時變化的東西,保持同步。之後就是載入佈局檔案,載入完畢之後就要初始化桌面上所有的佈局,然後載入桌布等操作,最後我們知道手機的桌面由於是應用頻率最高的Activity,所以系統添加了狀態儲存和恢復機制,savedInstanceState這個變數就是onCreate()方法中的那個Bundle型別的引數

if (!mRestoring) {
            if (sPausedFromUserAction) {
                // If the user leaves launcher, then we should just load items asynchronously when
                // they return.

                mModel.startLoader(true, -1);
            } else {
                // We only load the page synchronously if the user rotates (or triggers a
                // configuration change) while launcher is in the foreground

                mModel.startLoader(true, mWorkspace.getCurrentPage());
            }
        }

以上的這幾行程式碼就是Launcher載入桌面應用,包括載入應用選單列表中的應用的邏輯,可以看到它是通過LauncherModel物件呼叫startLoader方法來開始載入資料的。

onCreate()方法在這之後還有幾行程式碼,不過都不是太重要的在這裡就不在貼出來說了,下一篇就來詳細說說startLoader中的邏輯實現,同時系統的一些好的實現方法也值得我們在自己專案中來使用和學習。

歡迎關注我的微信公眾號,我會把一些生活的感想和投資方面的總結寫到公眾號,希望你能來和我一起交流技術之外的東西。

<

 張鶴的公眾號