標籤:android launcher 源碼
今天主要分析Android Launcher源碼中的一些重要類之間的關係,基本的載入流程。先來看一個類圖
Launcher.java 是主Activity 在onCreate方法裡面初始化了LauncherMode執行個體.
LauncherApplication app = ((LauncherApplication)getApplication());
mModel = app.setLauncher(this);
直接進入LauncherApplication.java的方法
LauncherModel setLauncher(Launcher launcher) { mModel.initialize(launcher); return mModel; }
這裡的mModel就是LauncherModel類了,LauncherModel扮演者重要的角色,實際上是個廣播,監控app的安裝,改變,和卸載另外就是載入所有app資訊
這裡Launcher實現了Callbacks介面,直接加入到callbacks列表中,後面的很多功能都要靠它回調處理
public void initialize(Callbacks callbacks) { synchronized (mLock) { mCallbacks = new WeakReference<Callbacks>(callbacks); } }
在Launcher.java onCreate的代碼中看下面的一段代碼.
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()); } }
源碼中注釋也顯示了2中情況,一種是目前使用者的介面不是案頭可能是某個應用程式介面 這樣我們通過後台非同步載入。
另外一種情況是 重新整理當前頁面的app資訊資料.
現在定位到LauncherMode.java源碼中startLoader方法 部分代碼如下.
if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) { mLoaderTask.runBindSynchronousPage(synchronousBindPage);//同步載入當前頁 } else { sWorkerThread.setPriority(Thread.NORM_PRIORITY); sWorker.post(mLoaderTask);//非同步載入 }和上面講的一樣,載入當前頁面資訊還是更新所有資訊. synchronousBindPage就是目前使用者滑動到第幾個螢幕,一共是5個螢幕.
我們用到了LoaderTask這個類是一個Runnable實現. 這個地方用的是HandlerThread (這個類我以前沒用過,一直是自己擷取Looper再處理的 )
這個類就是具體載入appinfo,appwidget, folder的資訊了。
我們看看LoaderTask run方法實現. 注意 LoaderTask 是LauncherMode的內部類.
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); } }
按照源碼的注釋 是第一次載入workspace , 第二次載入所有的app 資訊,你會發現 loadAndBindAllApps方法和loadAndBindWorkspace 2次調用是倒著的。
我的理解就是 先執行載入workspace 再執行載入all apps.
我們定位到 loadAndBindWorkspace方法中,
if (!mWorkspaceLoaded) { loadWorkspace(); synchronized (LoaderTask.this) { if (mStopped) { return; } mWorkspaceLoaded = true; } }
如果workspace還沒載入就調用loadWorkspace方法. 定位到這個方法裡面,一目瞭然發現 都是直接擷取應用程式資訊了包括widget.
直接調用了 PackageManager,AppWidgetManager,ContentResolver 擷取資訊.
讀取資訊儲存後 我們需要與workspace匯合,這個地方是調用bindWorkspace
在這個方面裡面我們會發現使用前面說的callbacks進行回調完整資料與View的綁定顯示.
/** * Binds all loaded data to actual views on the main thread. */ private void bindWorkspace(int synchronizeBindPage) { final long t = SystemClock.uptimeMillis(); Runnable r; // Don't use these two variables in any of the callback runnables. // Otherwise we hold a reference to them. final Callbacks oldCallbacks = mCallbacks.get(); if (oldCallbacks == null) { // This launcher has exited and nobody bothered to tell us. Just bail. Log.w(TAG, "LoaderTask running with no launcher"); return; } final boolean isLoadingSynchronously = (synchronizeBindPage > -1); final int currentScreen = isLoadingSynchronously ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen(); // Load all the items that are on the current page first (and in the process, unbind // all the existing workspace items before we call startBinding() below. unbindWorkspaceItemsOnMainThread(); ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>(); ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<LauncherAppWidgetInfo>(); HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>(); HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>(); synchronized (sBgLock) { workspaceItems.addAll(sBgWorkspaceItems); appWidgets.addAll(sBgAppWidgets); folders.putAll(sBgFolders); itemsIdMap.putAll(sBgItemsIdMap); } ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>(); ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>(); ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<LauncherAppWidgetInfo>(); ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<LauncherAppWidgetInfo>(); HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>(); HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>(); // Separate the items that are on the current screen, and all the other remaining items filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems, otherWorkspaceItems); filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets, otherAppWidgets); filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders, otherFolders); sortWorkspaceItemsSpatially(currentWorkspaceItems); sortWorkspaceItemsSpatially(otherWorkspaceItems); // Tell the workspace that we're about to start binding items r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.startBinding(); } } }; runOnMainThread(r); // Load items on the current page bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, currentFolders, null); if (isLoadingSynchronously) { r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.onPageBoundSynchronously(currentScreen); } } }; runOnMainThread(r); } // Load all the remaining pages (if we are loading synchronously, we want to defer this // work until after the first render) mDeferredBindRunnables.clear(); bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders, (isLoadingSynchronously ? mDeferredBindRunnables : null)); // Tell the workspace that we're done binding items r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.finishBindingItems(); } // If we're profiling, ensure this is the last thing in the queue. if (DEBUG_LOADERS) { Log.d(TAG, "bound workspace in " + (SystemClock.uptimeMillis()-t) + "ms"); } mIsLoadingAndBindingWorkspace = false; } }; if (isLoadingSynchronously) { mDeferredBindRunnables.add(r); } else { runOnMainThread(r); } }
這段代碼有很多需要解釋的,本質就是app,widgets, folders擷取並傳給主介面。今天就分析到這裡。後面還有很多,這次就分析到這裡,
如有問題 ,歡迎指出 謝謝。O(∩_∩)O~
Android Launcher源碼研究(二) 載入app流程1