接上次的。
首先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~