標籤:app 介面 android launcher 源碼
接上次的。
首先Launcher實現了LauncherModel.Callbacks介面,APP資訊資料載入成功後 ,回調介面把app資訊顯示到Launcher的 workspace介面上,這個過程代碼裡面稱為bind。
下面是個類調用過程的時序圖,不是很標準,不過能表達基本調用順序協助我們理解。
首先就是Launcher OnCreate中調用LauncherMode startLoader方法,這裡只看非同步方式 就是當前的頁面下標為-1,載入所有app資訊
mWorkspace.getCurrentPage()為-1的情況。
mModel.startLoader(true, -1);
在LauncherMode類的startLoader方法裡面,我們需要執行個體化線程類 LoaderTask,大部分工作都在這裡面完成.
run方法的代碼:
keep_running: { // Elevate priority when Home launches for the first time to avoid // starving at boot time. Staring at a blank home is not cool. synchronized (mLock) { if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " + (mIsLaunching ? "DEFAULT" : "BACKGROUND")); android.os.Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } if (loadWorkspaceFirst) { if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); loadAndBindWorkspace(); } else { if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps"); loadAndBindAllApps(); } if (mStopped) { break keep_running; } // Whew! Hard work done. Slow us down, and wait until the UI thread has // settled down. synchronized (mLock) { if (mIsLaunching) { if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND"); android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } } waitForIdle(); // second step if (loadWorkspaceFirst) { if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); loadAndBindAllApps(); } else { if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace"); loadAndBindWorkspace(); } // Restore the default thread priority after we are done loading items synchronized (mLock) { android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); } }
我們定位到 loadAndBindWorkspace 方法 載入workspace介面
private void loadAndBindWorkspace() { mIsLoadingAndBindingWorkspace = true; // Load the workspace if (DEBUG_LOADERS) { Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded); } if (!mWorkspaceLoaded) { //第一次載入 loadWorkspace(); //載入 synchronized (LoaderTask.this) { if (mStopped) { return; } mWorkspaceLoaded = true; } } // Bind the workspace bindWorkspace(-1);//綁定資料到UI }
</pre><p></p><p>loadWorkspace方法就是載入手機裡面的所有app資訊,包括app widget, folder 並且儲存到ArrayList中,後面顯示這些東西到UI上。</p><p>資料我們儲存好以後調用bindWorkspace(-1)開始顯示資料到workspace,裡麵包含了很多調用方法,主要是資料排序,清除以前綁定的UI資料。</p><p>在bindWorkspace方法裡面,調用了一些filter方法,這個地方主要是排序和過濾,分別判斷需要載入到那個螢幕裡面.</p><p></p><pre code_snippet_id="533506" snippet_file_name="blog_20141126_4_7063071" name="code" class="java"> r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.startBinding();//綁定之前,清理之前的資料,簡單就這麼理解 } } }; runOnMainThread(r);//UI更新需要在主線程中
下面這個方法就是把資料添加到workspace的螢幕中去.
bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, currentFolders, null);
private void bindWorkspaceItems(final Callbacks oldCallbacks, final ArrayList<ItemInfo> workspaceItems, final ArrayList<LauncherAppWidgetInfo> appWidgets, final HashMap<Long, FolderInfo> folders, ArrayList<Runnable> deferredBindRunnables) { final boolean postOnMainThread = (deferredBindRunnables != null); // Bind the workspace items int N = workspaceItems.size(); for (int i = 0; i < N; i += ITEMS_CHUNK) { final int start = i; final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i); final Runnable r = new Runnable() { @Override public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindItems(workspaceItems, start, start+chunkSize);//顯示app資訊到UI上 } } }; if (postOnMainThread) { deferredBindRunnables.add(r); } else { runOnMainThread(r); } } // Bind the folders if (!folders.isEmpty()) { final Runnable r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindFolders(folders);//顯示folders } } }; if (postOnMainThread) { deferredBindRunnables.add(r); } else { runOnMainThread(r); } } // Bind the widgets, one at a time N = appWidgets.size(); for (int i = 0; i < N; i++) { final LauncherAppWidgetInfo widget = appWidgets.get(i); final Runnable r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindAppWidget(widget);//綁定AppWidget資訊 } } }; if (postOnMainThread) { deferredBindRunnables.add(r); } else { runOnMainThread(r); } } }
我們這裡只看第一個綁定shortcut資訊代碼,其他2個其實道理差不多.
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) { setLoadOnResume(); // Get the list of added shortcuts and intersect them with the set of shortcuts here Set<String> newApps = new HashSet<String>(); newApps = mSharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps); Workspace workspace = mWorkspace; for (int i = start; i < end; i++) { final ItemInfo item = shortcuts.get(i); // Short circuit if we are loading dock items for a configuration which has no dock if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && mHotseat == null) { continue; } switch (item.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: ShortcutInfo info = (ShortcutInfo) item; String uri = info.intent.toUri(0).toString(); View shortcut = createShortcut(info); workspace.addInScreen(shortcut, item.container, item.screen, item.cellX, item.cellY, 1, 1, false); boolean animateIconUp = false; synchronized (newApps) { if (newApps.contains(uri)) { animateIconUp = newApps.remove(uri); } } if (animateIconUp) { // Prepare the view to be animated up shortcut.setAlpha(0f); shortcut.setScaleX(0f); shortcut.setScaleY(0f); mNewShortcutAnimatePage = item.screen; if (!mNewShortcutAnimateViews.contains(shortcut)) { mNewShortcutAnimateViews.add(shortcut); } } break; case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), (FolderInfo) item, mIconCache); workspace.addInScreen(newFolder, item.container, item.screen, item.cellX, item.cellY, 1, 1, false); break; } } workspace.requestLayout(); }
重點就是下面這個,把資料畫到UI上面,座標資訊,APP資訊.
workspace.addInScreen(shortcut, item.container, item.screen, item.cellX, item.cellY, 1, 1, false);
最後我們調用 finishBindingItems完成調用,這個方法裡面還包括了滑動和顯示APP動畫方法的處理。
if (mVisible || mWorkspaceLoading) { Runnable newAppsRunnable = new Runnable() { @Override public void run() { runNewAppsAnimation(false); } }; boolean willSnapPage = mNewShortcutAnimatePage > -1 && mNewShortcutAnimatePage != mWorkspace.getCurrentPage(); if (canRunNewAppsAnimation()) { // If the user has not interacted recently, then either snap to the new page to show // the new-apps animation or just run them if they are to appear on the current page if (willSnapPage) { mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable); } else { runNewAppsAnimation(false); } } else { // If the user has interacted recently, then just add the items in place if they // are on another page (or just normally if they are added to the current page) runNewAppsAnimation(willSnapPage); } }
詳細的我們到後面繼續分析。還包括具體Workspace上面的介面格子cell是怎麼設計和APP添加位置的計算問題。
若有問題,請指出,謝謝~ O(∩_∩)O~
Android Launcher源碼研究(三) 載入app流程2