今天主要分析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~