Launcher和LauncherModel之间的数据交互
2015-12-30 19:50
507 查看
android的launcher中的数据主要是通过launcherModel这个类来处理的,model主要负责和xml文件及数据库的书记交互。
launcher的数据处理从launcher这个activity创建时就已经开始了:
1、launcher.java中的oncreate方法,调用了launchermodel中的startloader方法,开始加载launcher桌面需要的数据
2、去startloader中看看做了写什么
我们可以看到,在这个方法中主要是创建了LoaderTask对象,并把它放到了消息队列中让系统处理。
我们接下来看看LoaderTask中的run方法做了什么事情
这里代码比较多,先看重点,这里主要是调用了三个方法,首先是loadAndBindWorkspace(),这个方法是加载并绑定workspace以及它上面的快捷方式图标、小组件和文件夹。
然后是waitForIdle()方法,这个方法还没有仔细看,貌似是空闲时加载,最后一个是loadAndBindAllApps()方法,就是加载并绑定所有的应用程序至launcher。
今天主要看一下loadAndBindWorkspace()方法。
这个方法拆分成了加载和绑定两个方法,loadWorkspace和bindWorkspace,load方法主要负责加载数据库中的数据到相应的容器中,可以看到,当第一次创建数据库时,会将预设页面中的item数据从xml文件加载至数据库中,先将item数据存入favorites表中,然后再将获取的screenId和生成的screenIndex存入workspacsreens表中。而bindWorkspace则是将从数据库中load出来的数据item显示在屏幕上。
bind方法是通过回调将容器中的数据绑定至ui。具体方法:
在bind方法中首先复制了一份加载的数据,然后解绑当前workspace的所有item(源码中其实什么也没做),然后将上述容器中的内容分为两类,一类为当前页的,一类为其他页的。并且还做了一些其它处理如排序等。
接着就是通知ui我要开始绑定了。需要注意的是这是通过handle向ui线程发送一个runnable并在run中回调callback中的接口方法startBinding实现的,具体代码如下:
之后就开始回调workspace的数据了,首先是屏幕数据
也是一样的方式,后面还有很多。下面就是绑定当前页面和其它页面:
最后就是通知ui绑定结束,做一些收尾工作:
在launcher.java中通过实现launchermodel中的callbakcs接口来获取回调的数据,从而显示在屏幕上。
当然具体的过程还是比较复杂的,这里只阐述了大抵的过程,以后有机会的话,会更加详细看一下。
launcher的数据处理从launcher这个activity创建时就已经开始了:
1、launcher.java中的oncreate方法,调用了launchermodel中的startloader方法,开始加载launcher桌面需要的数据
if (!mRestoring) { /// M: Add for smart book feature. Reset load state if database changed before. if (isDatabaseIdChanged) { mModel.resetLoadedState(true, true); } else { /**M: Added to reset the loader state, to resolve the timing state issue.@{*/ mModel.resetLoadedState(false, false); /**@}**/ } if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) { // If the user leaves launcher, then we should just load items asynchronously when // they return. mModel.startLoader(true, 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(true, mWorkspace.getRestorePage()); }
2、去startloader中看看做了写什么
public void startLoader(boolean isLaunching, int synchronousBindPage) { startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE); } public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) { synchronized (mLock) { if (DEBUG_LOADERS) { LauncherLog.d(TAG, "startLoader: isLaunching=" + isLaunching + ", mCallbacks = " + mCallbacks); } // Clear any deferred bind-runnables from the synchronized load process // We must do this before any loading/binding is scheduled below. synchronized (mDeferredBindRunnables) { mDeferredBindRunnables.clear(); } // Don't bother to start the thread if we know it's not going to do anything if (mCallbacks != null && mCallbacks.get() != null) { // If there is already one running, tell it to stop. // also, don't downgrade isLaunching if we're already running isLaunching = isLaunching || stopLoaderLocked(); /// M: added for top package feature, load top packages from a xml file. AllAppsList.loadTopPackage(mApp.getContext()); mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags); if (LauncherLog.DEBUG) { LauncherLog.d(TAG, "startLoader: mAllAppsLoaded = " + mAllAppsLoaded + ",mWorkspaceLoaded = " + mWorkspaceLoaded + ",synchronousBindPage = " + synchronousBindPage + ",mIsLoaderTaskRunning = " + mIsLoaderTaskRunning + ",mLoaderTask = " + mLoaderTask, new Throwable("startLoader")); } if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE && mAllAppsLoaded && mWorkspaceLoaded) { mLoaderTask.runBindSynchronousPage(synchronousBindPage); } else { sWorkerThread.setPriority(Thread.NORM_PRIORITY); sWorker.post(mLoaderTask); } } } }
我们可以看到,在这个方法中主要是创建了LoaderTask对象,并把它放到了消息队列中让系统处理。
我们接下来看看LoaderTask中的run方法做了什么事情
public void run() { boolean isUpgrade = false; synchronized (mLock) { if (DEBUG_LOADERS) { LauncherLog.d(TAG, "Set load task running flag >>>>, mIsLaunching = " + mIsLaunching + ",this = " + this); } mIsLoaderTaskRunning = true; } // Optimize for end-user experience: if the Launcher is up and // running with the // All Apps interface in the foreground, load All Apps first. Otherwise, load the // workspace first (default). 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 (DEBUG_LOADERS) LauncherLog.d(TAG, "step 1: loading workspace"); isUpgrade = loadAndBindWorkspace(); if (mStopped) { LauncherLog.i(TAG, "LoadTask break in the middle, this = " + this); 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) LauncherLog.d(TAG, "Setting thread priority to BACKGROUND"); android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } } waitForIdle(); // second step if (DEBUG_LOADERS) LauncherLog.d(TAG, "step 2: loading all apps"); loadAndBindAllApps(); // Restore the default thread priority after we are done loading items synchronized (mLock) { android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); } } // Update the saved icons if necessary if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons"); synchronized (sBgLock) { for (Object key : sBgDbIconCache.keySet()) { updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key)); } sBgDbIconCache.clear(); } if (LauncherAppState.isDisableAllApps()) { // Ensure that all the applications that are in the system are // represented on the home screen. if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) { verifyApplications(); } } // Clear out this reference, otherwise we end up holding it until all of the // callback runnables are done. mContext = null; synchronized (mLock) { // If we are still the last one to be scheduled, remove ourselves. if (mLoaderTask == this) { mLoaderTask = null; } if (DEBUG_LOADERS) { LauncherLog.d(TAG, "Reset load task running flag <<<<, this = " + this); } mIsLoaderTaskRunning = false; } }
这里代码比较多,先看重点,这里主要是调用了三个方法,首先是loadAndBindWorkspace(),这个方法是加载并绑定workspace以及它上面的快捷方式图标、小组件和文件夹。
然后是waitForIdle()方法,这个方法还没有仔细看,貌似是空闲时加载,最后一个是loadAndBindAllApps()方法,就是加载并绑定所有的应用程序至launcher。
今天主要看一下loadAndBindWorkspace()方法。
/** Returns whether this is an upgrade path */ private boolean loadAndBindWorkspace() { mIsLoadingAndBindingWorkspace = true; // Load the workspace if (DEBUG_LOADERS) { Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded); } boolean isUpgradePath = false; if (!mWorkspaceLoaded) { isUpgradePath = loadWorkspace(); synchronized (LoaderTask.this) { if (mStopped) { LauncherLog.d(TAG, "loadAndBindWorkspace returned by stop flag."); return isUpgradePath; } mWorkspaceLoaded = true; } } // Bind the workspace bindWorkspace(-1, isUpgradePath); return isUpgradePath; }
这个方法拆分成了加载和绑定两个方法,loadWorkspace和bindWorkspace,load方法主要负责加载数据库中的数据到相应的容器中,可以看到,当第一次创建数据库时,会将预设页面中的item数据从xml文件加载至数据库中,先将item数据存入favorites表中,然后再将获取的screenId和生成的screenIndex存入workspacsreens表中。而bindWorkspace则是将从数据库中load出来的数据item显示在屏幕上。
// sBgWorkspaceItems is passed to bindItems, which expects a list of all folders and shortcuts // created by LauncherModel that are directly on the home screen (however, no widgets or // shortcuts within folders). static final ArrayList<ItemInfo> sBgWorkspaceItems = new ArrayList<ItemInfo>(); /// M: sBgAddAppItems record apps added to database for that when add item to DB not finish /// but need to bind items. static final ArrayList<AppInfo> sBgAddAppItems = new ArrayList<AppInfo>(); /// M: sBgAddAppItems record apps added to database for that when delete item in DB not finish /// but need to bind items. static final ArrayList<AppInfo> sBgDelAppItems = new ArrayList<AppInfo>(); // sBgAppWidgets is all LauncherAppWidgetInfo created by LauncherModel. Passed to bindAppWidget() static final ArrayList<LauncherAppWidgetInfo> sBgAppWidgets = new ArrayList<LauncherAppWidgetInfo>(); // sBgFolders is all FolderInfos created by LauncherModel. Passed to bindFolders() static final HashMap<Long, FolderInfo> sBgFolders = new HashMap<Long, FolderInfo>();
bind方法是通过回调将容器中的数据绑定至ui。具体方法:
/** * Binds all loaded data to actual views on the main thread. */ private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) { 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; } // Save a copy of all the bg-thread collections 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>(); ArrayList<Long> orderedScreenIds = new ArrayList<Long>(); synchronized (sBgLock) { workspaceItems.addAll(sBgWorkspaceItems); appWidgets.addAll(sBgAppWidgets); folders.putAll(sBgFolders); itemsIdMap.putAll(sBgItemsIdMap); orderedScreenIds.addAll(sBgWorkspaceScreens); } final boolean isLoadingSynchronously = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE; int currScreen = isLoadingSynchronously ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen(); if (currScreen >= orderedScreenIds.size()) { // There may be no workspace screens (just hotseat items and an empty page). currScreen = PagedView.INVALID_RESTORE_PAGE; } final int currentScreen = currScreen; final long currentScreenId = currentScreen < 0 ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen); // 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(); // Separate the items that are on the current screen, and all the other remaining items 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>(); /// M. ALPS01916589, filter the right fist screen. int tempCurrentScreen; if (orderedScreenIds.size() != 0 && currentScreen >= 0 && currentScreen < orderedScreenIds.size()) { tempCurrentScreen = orderedScreenIds.get(currentScreen).intValue(); } else { tempCurrentScreen = currentScreen; } filterCurrentWorkspaceItems(tempCurrentScreen, workspaceItems, currentWorkspaceItems, otherWorkspaceItems); filterCurrentAppWidgets(tempCurrentScreen, appWidgets, currentAppWidgets, otherAppWidgets); filterCurrentFolders(tempCurrentScreen, itemsIdMap, folders, currentFolders, otherFolders); /// M. 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, MAIN_THREAD_BINDING_RUNNABLE); bindWorkspaceScreens(oldCallbacks, orderedScreenIds); // 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 && currentScreen != PagedView.INVALID_RESTORE_PAGE) { /** by xuhang callbacks.onPageBoundSynchronously(currentScreen); */ } } }; runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); } // Load all the remaining pages (if we are loading synchronously, we want to defer this // work until after the first render) synchronized (mDeferredBindRunnables) { 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(isUpgradePath); * by xuhang */ } // 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) { synchronized (mDeferredBindRunnables) { mDeferredBindRunnables.add(r); } } else { runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); } }
在bind方法中首先复制了一份加载的数据,然后解绑当前workspace的所有item(源码中其实什么也没做),然后将上述容器中的内容分为两类,一类为当前页的,一类为其他页的。并且还做了一些其它处理如排序等。
// Separate the items that are on the current screen, and all the other remaining items 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>(); /// M. ALPS01916589, filter the right fist screen. int tempCurrentScreen; if (orderedScreenIds.size() != 0 && currentScreen >= 0 && currentScreen < orderedScreenIds.size()) { tempCurrentScreen = orderedScreenIds.get(currentScreen).intValue(); } else { tempCurrentScreen = currentScreen; } filterCurrentWorkspaceItems(tempCurrentScreen, workspaceItems, currentWorkspaceItems, otherWorkspaceItems); filterCurrentAppWidgets(tempCurrentScreen, appWidgets, currentAppWidgets, otherAppWidgets); filterCurrentFolders(tempCurrentScreen, itemsIdMap, folders, currentFolders, otherFolders); /// M. sortWorkspaceItemsSpatially(currentWorkspaceItems); sortWorkspaceItemsSpatially(otherWorkspaceItems);
接着就是通知ui我要开始绑定了。需要注意的是这是通过handle向ui线程发送一个runnable并在run中回调callback中的接口方法startBinding实现的,具体代码如下:
// 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, MAIN_THREAD_BINDING_RUNNABLE);
之后就开始回调workspace的数据了,首先是屏幕数据
private void bindWorkspaceScreens(final Callbacks oldCallbacks, final ArrayList<Long> orderedScreens) { final Runnable r = new Runnable() { @Override public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.bindScreens(orderedScreens); } } }; runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); }
也是一样的方式,后面还有很多。下面就是绑定当前页面和其它页面:
// 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 && currentScreen != PagedView.INVALID_RESTORE_PAGE) { /** by xuhang callbacks.onPageBoundSynchronously(currentScreen); */ } } }; runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); } // Load all the remaining pages (if we are loading synchronously, we want to defer this // work until after the first render) synchronized (mDeferredBindRunnables) { mDeferredBindRunnables.clear(); } bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders, (isLoadingSynchronously ? mDeferredBindRunnables : null));
最后就是通知ui绑定结束,做一些收尾工作:
// Tell the workspace that we're done binding items r = new Runnable() { public void run() { Callbacks callbacks = tryGetCallbacks(oldCallbacks); if (callbacks != null) { callbacks.finishBindingItems(isUpgradePath); } // 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) { synchronized (mDeferredBindRunnables) { mDeferredBindRunnables.add(r); } } else { runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE); }
在launcher.java中通过实现launchermodel中的callbakcs接口来获取回调的数据,从而显示在屏幕上。
当然具体的过程还是比较复杂的,这里只阐述了大抵的过程,以后有机会的话,会更加详细看一下。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories