您的位置:首页 > 移动开发 > Android开发

Launcher和LauncherModel之间的数据交互

2015-12-30 19:50 507 查看
android的launcher中的数据主要是通过launcherModel这个类来处理的,model主要负责和xml文件及数据库的书记交互。

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接口来获取回调的数据,从而显示在屏幕上。

当然具体的过程还是比较复杂的,这里只阐述了大抵的过程,以后有机会的话,会更加详细看一下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  launcher android