android7.x Launcher3原始碼解析(3)---workspace和allapps載入流程

來源:互聯網
上載者:User

標籤:recycle   first   shm   parent   second   pen   pack   返回   轉譯   

Launcher系列目錄:
一、android7.x Launcher3原始碼解析(1)—啟動流程
二、android7.x Launcher3原始碼解析(2)—架構結構
三、android7.x Launcher3原始碼解析(3)—workspace和allapps載入流程

前兩篇部落格分別對Lancher的啟動和Launcher的架構結構進行了一些分析。這一篇。將著重開始分析介面的載入流程。

1、總體流程

先上一張總體的流程圖吧。(圖片看不清能夠下載下來看或者右擊新開個頁面查看圖片)

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvUGljYXNzb19M/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="這裡寫圖片描寫敘述" title="">

先從Launcher.java的onCreate方法開始。

protected void onCreate(Bundle savedInstanceState) {    ......    //建立LauncherAppState對象    LauncherAppState.setApplicationContext(getApplicationContext());    LauncherAppState app = LauncherAppState.getInstance();    ......    //建立LauncherModel對象    mModel = app.setLauncher(this);    //一些其它對象初始化    ......    setContentView(R.layout.launcher);    setupViews();    if (!mRestoring) {            if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {                // If the user leaves launcher, then we should just load items asynchronously when                // they return.                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);            } else {                // We only load the page synchronously if the user rotates (or triggers a                // configuration change) while launcher is in the foreground                mModel.startLoader(mWorkspace.getRestorePage());            }    }    ......}

重點調用了LauncherModel的startLoader的方法,startLoader裡面。最重要的就是啟動了LoaderTask。mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);

我們接著分析LoaderTask的run方法。

public void run() {            ......            keep_running: {                if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");                loadAndBindWorkspace();                if (mStopped) {                    break keep_running;                }                waitForIdle();                // second step                if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");                loadAndBindAllApps();                waitForIdle();                // third step                if (DEBUG_LOADERS) Log.d(TAG, "step 3: loading deep shortcuts");                loadAndBindDeepShortcuts();            }            ......        }

這裡一共就幾步,loadAndBindWorkspace–>waitForIdle()—>loadAndBindAllApps()—>waitForIdle()—>loadAndBindDeepShortcuts()
3步載入流程裡面都穿插了waitForIdle,這種方法是幹嘛的呢?

private void waitForIdle() {          ......            synchronized (LoaderTask.this) {                final long workspaceWaitTime = DEBUG_LOADERS ?

SystemClock.uptimeMillis() : 0; mHandler.postIdle(new Runnable() { public void run() { synchronized (LoaderTask.this) { mLoadAndBindStepFinished = true; if (DEBUG_LOADERS) { Log.d(TAG, "done with previous binding step"); } LoaderTask.this.notify(); } } }); while (!mStopped && !mLoadAndBindStepFinished) { try { // Just in case mFlushingWorkerThread changes but we aren‘t woken up, // wait no longer than 1sec at a time this.wait(1000); } catch (InterruptedException ex) { // Ignore } } ...... } }

load資料時我們是去UI線程中處理的,UI線程正常情況下是不能堵塞的,否則有可能產生ANR。這將嚴重影響使用者體驗。

全部這裡LoaderTask在將結果發送給UI線程之後,為了保證介面綁定任務能夠高效的完畢,往往會將自己的任務暫停下來,等待UI線程處理完畢。
分析下這種方法:
首先。建立一個UI線程閑時啟動並執行任務,這個任務負責設定某些關鍵的控制標誌。並將其通過PostIdle方法增加處理器的訊息佇列中。一旦任務得到運行。就會將mLoadAndBindStepFinished 置為true,以控制即將來臨的有條件的無限等待。 最後 設定一個有條件的無限等待,等待來自UI線程的指示。

2、workspace的載入流程

從總流程圖上能夠看到,workspace的載入流程主要分為loadWorkspace();bindWorkspace(mPageToBindFirst);

a、loadWorkspace()

loadWorkspace()的代碼實在是太多了,這裡就不全部貼出來了。主要功能就是負責從資料庫表中讀取資料並轉譯為Launcher案頭項的資料結構。
以下為步驟:
1、進行一些預先處理
2、載入預設值

LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary()

之前Launcher2loadDefaultFavoritesIfNecessary這種方法,是這樣載入預設布局的:

workspaceResId = sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, R.xml.default_workspace);

可是Launcher3中,是這樣載入的:

int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,                            "xml", partner.getPackageName());

這個地方,我臨時還沒理解。究竟預設布局是哪個?

3、初始化資料
清空之前的記憶體資料

/** Clears all the sBg data structures */        private void clearSBgDataStructures() {            synchronized (sBgLock) {                sBgWorkspaceItems.clear();                sBgAppWidgets.clear();                sBgFolders.clear();                sBgItemsIdMap.clear();                sBgWorkspaceScreens.clear();            }        }

4、查詢ContentProvider,返回favorites表的結果集

final HashMap<String, Integer> installingPkgs = PackageInstallerCompat                        .getInstance(mContext).updateAndGetActiveSessionCache();                final ArrayList<Long> itemsToRemove = new ArrayList<Long>();                final ArrayList<Long> restoredRows = new ArrayList<Long>();                final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;                if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);                final Cursor c = contentResolver.query(contentUri, null, null, null, null);

5、依據不同的類型,將資料儲存到相應的arrayList中。
類型包括以下這幾種:

ITEM_TYPE_APPLICATIONITEM_TYPE_SHORTCUTITEM_TYPE_FOLDERITEM_TYPE_APPWIDGETITEM_TYPE_CUSTOM_APPWIDGET

依據以上類型,將資料儲存到一下全域變數裡面
sBgItemsIdMap,sBgWorkspaceItemsm,sBgAppWidgets,sBgFolders

另外。假設有空的目錄、空的螢幕,也會delete掉,終於,把全部螢幕載入進全域變數sBgWorkspaceScreens中。

......           // Remove any empty folder                    for (long folderId : LauncherAppState.getLauncherProvider()                            .deleteEmptyFolders()) {                        sBgWorkspaceItems.remove(sBgFolders.get(folderId));                        sBgFolders.remove(folderId);                        sBgItemsIdMap.remove(folderId);                    }                    ...... sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));                // Remove any empty screens                ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);                for (ItemInfo item: sBgItemsIdMap) {                    long screenId = item.screenId;                    if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&                            unusedScreens.contains(screenId)) {                        unusedScreens.remove(screenId);                    }                }                // If there are any empty screens remove them, and update.                if (unusedScreens.size() != 0) {                    sBgWorkspaceScreens.removeAll(unusedScreens);                    updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);                }
b、bindWorkspace

bindWorkspace的功能是將上面擷取到的資料由Launcher顯示出來。
步驟:
1、首先複製資料:

            synchronized (sBgLock) {                workspaceItems.addAll(sBgWorkspaceItems);                appWidgets.addAll(sBgAppWidgets);                orderedScreenIds.addAll(sBgWorkspaceScreens);                folders = sBgFolders.clone();                itemsIdMap = sBgItemsIdMap.clone();            }

2、裝載資料並排序

            //裝載案頭項資料            filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,                    otherWorkspaceItems);            //裝載widget                    filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,                    otherAppWidgets);            //裝載目錄            filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,                    otherFolders);            //排序                    sortWorkspaceItemsSpatially(currentWorkspaceItems);            sortWorkspaceItemsSpatially(otherWorkspaceItems);

3、開始綁定

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvUGljYXNzb19M/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="這裡寫圖片描寫敘述" title="">

就上個流程圖吧。

3、應用程式apps的載入

載入apps的主要流程。最上面那張流程圖已經給出了,假設全部app沒有載入,則loadAllApps();,不然直接onlyBindAllApps();

1、loadAllApps()

依據代碼,畫了下流程圖,可是我不明確userProfile是個什麼鬼?

2、onlyBindAllApps()

這裡的函數比綁定workspace簡單多了,直接通知Launcher綁定

            Runnable r = new Runnable() {                public void run() {                    final long t = SystemClock.uptimeMillis();                    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);                    if (callbacks != null) {                        callbacks.bindAllApplications(list);                        callbacks.bindAllPackages(widgetList);                    }                    if (DEBUG_LOADERS) {                        Log.d(TAG, "bound all " + list.size() + " apps from cache in "                                + (SystemClock.uptimeMillis()-t) + "ms");                    }                }            };

看一下Launcher的bindAllApplications函數。當然這個函數在loadAllApps函數裡面也有。就是怎樣綁定資料來顯示呢?
Launcher.java的bindAllApplications函數裡面會給AllAppsContainerView設定資料

if (mAppsView != null) {            mAppsView.setApps(apps);        }

再跟代碼到AllAppsContainerView。

public void setApps(List<AppInfo> apps) {        mApps.setApps(apps);    }

這個apps在哪裡用到呢?比較明顯的就是onFinishInflate()函數,

.....        // Load the all apps recycler view        mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);        mAppsRecyclerView.setApps(mApps);        mAppsRecyclerView.setLayoutManager(mLayoutManager);        mAppsRecyclerView.setAdapter(mAdapter);        mAppsRecyclerView.setHasFixedSize(true);.....

設定給了recyclerView。非常顯然,Launcher的應用程式介面就是一個自己定義的RecyclerView,給這個recyclerview綁定的adpter是AllAppsGridAdapter。看一下這個adpter的onCreateViewHolder函數,非常明顯。這裡依據不同的類型載入了不同的布局(應用程式介面有app和目錄。頭上還有個搜尋方塊。用recyclerview的這個功能是最easy實現的),關於Recyclerview怎樣能夠依據不同的類型載入不同的布局,能夠參考我非常久之前寫的部落格 RecyclerView的不同position載入不同View實現。

好了,Launcher3的workspace和應用程式apps的載入流程就說到這,後面還會對Launcher裡面的內容做詳細的分析。

android7.x Launcher3原始碼解析(3)---workspace和allapps載入流程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.