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

Android 4.0 Launcher2源码分析——Launcher内容加载详细过程

2016-12-16 11:10 731 查看
本文来自http://blog.csdn.net/chenshaoyang0011 转载请申明文章出处!

文中如有纰漏之处,望不吝指教~~~欢迎讨论,共同学习~~~

Launcher在应用启动的时候,需要加载AppWidget,shortcut等内容项,通过调用LauncherModel.startLoader(),开始加载的工作。launcherModel中加载好的内容会通过

LauncherModel.Callbacks接口的回调函数将数据传给需要的组件,那先来看看Callbacks的定义:

[java]
view plain
copy

public interface Callbacks {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void startBinding();
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
public void bindFolders(HashMap<Long,FolderInfo> folders);
public void finishBindingItems();
public void bindAppWidget(LauncherAppWidgetInfo info);
public void bindAllApplications(ArrayList<ApplicationInfo> apps);
public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);
public void bindPackagesUpdated();
public boolean isAllAppsVisible();
public void bindSearchablesChanged();
}

简单的了解下每个方法的用途:
setLoadOnResume() 由于Launcher继承自Activity,因此Launcher可能会处于paused状态(onPause()被调用),则有可能在这段时间内资源可能

发生了改变,如应用被删除或新应用安装,因此需要在onResume()中调用此方法进行重新加载。

getCurrentWorkspace() 获取当前屏幕的序号

startBinding() 通知Launcher加载开始,并更新Workspace上的shortcuts

bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) 加载一批内容项到Workspace,加载的内容项包括,Application、shortcut、folder。

bindFolders(HashMap<Long, FolderInfo> folders) 加载folder的内容

finishBindingItems() 通知Launcher加载结束。

bindAppWidget(LauncherAppWidgetInfo item) 加载AppWidget到Workspace

bindAllApplications(final ArrayList<ApplicationInfo> apps) 在All Apps页加载所有应用的Icon

bindAppsAdded(ArrayList<ApplicationInfo> apps) 通知Launcher一个新的应用被安装,并加载这个应用

bindAppsUpdated(ArrayList<ApplicationInfo> apps) 通知Launcher一个应用发生了更新

bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent) 通知Launcher一个应用被删除了

bindPackagesUpdated() 通知Launcher多个应用发生了更新

isAllAppsVisible()用于在加载的过程中记录当前Launcher的状态,返回true则当前显示的All Apps

bindSearchablesChanged()当搜索/删除框状态发生改变时调用

了解了每个方法的作用之后,就可以开始进一步的分析了。

首先让我们回顾一下整个加载过程的流程是怎样的



通过在Launcher中调用LauncherModel.startLoader()方法,开始加载内容。

[java]
view plain
copy

public void startLoader(Context context, boolean isLaunching) {
synchronized (mLock) {
......
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
......
mLoaderTask = new LoaderTask(context, isLaunching);
sWorkerThread.setPriority(Thread.NORM_PRIORITY);
sWorker.post(mLoaderTask);
}
}
}

mLoaderTask是一个Runnable,被添加到消息队列之后,它的run() 方法会被调用。

[java]
view plain
copy

public void run() {
......
keep_running: {
......
if (loadWorkspaceFirst) {
......
loadAndBindWorkspace();
} else {
......
}

if (mStopped) {
break keep_running;
}

......
waitForIdle();

// second step
if (loadWorkspaceFirst) {
......
loadAndBindAllApps();
} else {
......
}
......
}
......
}

加载的工作由两部分组成,第一部分是为Workspace加载内容,第二部分则是为AllApps加载内容。每一部分的加载又可以分为两个步骤:1、由LauncherModel完成,主要

工作是从数据库中读取信息,并且按类别将内容项分装到不同的数据结构中。2、由Launcher来完成,通过LauncherModel.Callbacks接口定义的回调方法,从LauncherModel

中获取的数据,将其显示到桌面。

一、Workspace内容加载

run()中首先会调用loadAndBindWorkspace()方法开始Workspace的加载工作。

[java]
view plain
copy

private void loadAndBindWorkspace() {
...
if (!mWorkspaceLoaded) {
loadWorkspace();
synchronized (LoaderTask.this) {
if (mStopped) {
return;
}
mWorkspaceLoaded = true;
}
}

// Bind the workspace
bindWorkspace();
}

因为WorkspaceLoaded=false,所以会调用loadWorkspace()读取内容数据,等数据读取完毕之后,再调用bindWorkspace()将数据

加载到Workspace中。

[java]
view plain
copy

private void loadWorkspace() {
......

//存放container为CONTAINER_DESKTOP和CONTAINER_HOTSEAT类型的item
sWorkspaceItems.clear();

//存放所有的AppWidget类型
sAppWidgets.clear();

//存放的FolderInfo.id和FolderInfo组成的映射对
sFolders.clear();

//所有的item的id和ItemInfo组成的映射对
sItemsIdMap.clear();
sDbIconCache.clear();

final ArrayList<Long> itemsToRemove = new ArrayList<Long>();

final Cursor c = contentResolver.query(
LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);

// +1 for the hotseat (it can be larger than the workspace)
// Load workspace in reverse order to ensure that latest items are loaded first (and
// before any earlier duplicates)
//代表屏幕中的每一个单位的方格是否被占用。
//第一维表示分屏的序号,其中最后一个代表Hotseat
//第二维表示x方向方格的序号
//第三维表示y方向方格的序号
final ItemInfo occupied[][][] =
new ItemInfo[Launcher.SCREEN_COUNT + 1][mCellCountX + 1][mCellCountY + 1];

try {
......

while (!mStopped && c.moveToNext()) {
try {
int itemType = c.getInt(itemTypeIndex);

switch (itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
intentDescription = c.getString(intentIndex);
try {
intent = Intent.parseUri(intentDescription, 0);
} catch (URISyntaxException e) {
continue;
}

if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
info = getShortcutInfo(manager, intent, context, c, iconIndex,
titleIndex, mLabelCache);
} else {
info = getShortcutInfo(c, context, iconTypeIndex,
iconPackageIndex, iconResourceIndex, iconIndex,
titleIndex);
}

if (info != null) {
......

// check & update map of what's occupied
//检查这个item所占的空间是否空闲,true表示空闲
if (!checkItemPlacement(occupied, info)) {
break;
}

switch (container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP:
case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
//当加载的item类型为ITEM_TYPE_APPLICATION或者ITEM_TYPE_SHORTCUT
//并且所属的container为CONTAINER_DESKTOP或者CONTAINER_HOTSEAT时
//将其添加到sWorkspaceItems中
sWorkspaceItems.add(info);
break;
default:
// Item is in a user folder
//如果item的container不是上述两者,则代表它处于一个folder中
//将其添加到所属的folderInfo中
FolderInfo folderInfo =
findOrMakeFolder(sFolders, container);
folderInfo.add(info);
break;
}
//所有的ITEM_TYPE_APPLICATION和ITEM_TYPE_SHORTCUT类型的item都需要
//加入到sItemsIdMap的映射对中。
sItemsIdMap.put(info.id, info);

// now that we've loaded everthing re-save it with the
// icon in case it disappears somehow.
queueIconToBeChecked(sDbIconCache, info, c, iconIndex);
} else {
// Failed to load the shortcut, probably because the
// activity manager couldn't resolve it (maybe the app
// was uninstalled), or the db row was somehow screwed up.
// Delete it.
id = c.getLong(idIndex);
Log.e(TAG, "Error loading shortcut " + id + ", removing it");
contentResolver.delete(LauncherSettings.Favorites.getContentUri(
id, false), null, null);
}
break;

case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
id = c.getLong(idIndex);
FolderInfo folderInfo = findOrMakeFolder(sFolders, id);
.....
// check & update map of what's occupied
if (!checkItemPlacement(occupied, folderInfo)) {
break;
}
switch (container) {
case LauncherSettings.Favorites.CONTAINER_DESKTOP:
case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
//folderInfo类型的item也需要添加到sWorkspaceItems中
sWorkspaceItems.add(folderInfo);
break;
}
//添加到sItemsIdMap映射对中
sItemsIdMap.put(folderInfo.id, folderInfo);
//添加到sFolder映射对中
sFolders.put(folderInfo.id, folderInfo);
break;

case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
// Read all Launcher-specific widget details
int appWidgetId = c.getInt(appWidgetIdIndex);
id = c.getLong(idIndex);

final AppWidgetProviderInfo provider =
widgets.getAppWidgetInfo(appWidgetId);

if (!isSafeMode && (provider == null || provider.provider == null ||
provider.provider.getPackageName() == null)) {
......
itemsToRemove.add(id);
} else {
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId);
......

container = c.getInt(containerIndex);
......
appWidgetInfo.container = c.getInt(containerIndex);

// check & update map of what's occupied
if (!checkItemPlacement(occupied, appWidgetInfo)) {
break;
}
//添加到sItemsIdMap映射对
sItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
//添加到sAppWidgets
sAppWidgets.add(appWidgetInfo);
}
break;
}
} catch (Exception e) {
......
}
}
} finally {
c.close();
}

......
}

loadWorkspace的工作就是从ContentProvider获取指定URI中的数据,并将它们分类存放到指定的数据结构中。分类的标准有两条:1、item的类型。包括ITEM_TYPE_APPLICATION
,ITEM_TYPE_SHORTCUT ,ITEM_TYPE_FOLDER,ITEM_TYPE_APPWIDGET四类。2、item所属的容器。包括CONTAINER_DESKTOP,

CONTAINER_HOTSEAT以及其它(主要指文件夹)。LauncherModel在读取完数据之后,通过LauncherModel.bindWorkspace()将数据传给到Launcher。进入LauncherModel.bindWorkspace()中:

[java]
view plain
copy

private void bindWorkspace() {
......
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
//开始绑定
callbacks.startBinding();
}
}
});

......
for (int i=0; i<N; i+=ITEMS_CHUNK) {
final int start = i;
final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
//绑定application、shortcut、folder三种内容
callbacks.bindItems(workspaceItems, start, start+chunkSize);
}
}
});
}
......
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
//绑定folder
callbacks.bindFolders(folders);
}
}
});
......
for (int i=0; i<N; i++) {
final LauncherAppWidgetInfo widget = sAppWidgets.get(i);
if (widget.screen == currentScreen) {
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
//绑定当前屏的AppWidget
callbacks.bindAppWidget(widget);
}
}
});
}
}

for (int i=0; i<N; i++) {
final LauncherAppWidgetInfo widget = sAppWidgets.get(i);
if (widget.screen != currentScreen) {
mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
//绑定其它屏的AppWidget
callbacks.bindAppWidget(widget);
}
}
});
}
}

mHandler.post(new Runnable() {
public void run() {
Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
//结束绑定
callbacks.finishBindingItems();
}
}
});
......
}

可以看到,Launcher的内容绑定分为五步:分别对应着startBinding()、bindItems()、bindFolders()、bindAppWidgets()、finishBindingItems()的调用

Step1:调用Callbacks.startBinding()

由于Launcher实现了Callbacks接口,Launcher中的startBinding()被调用,进入Launcher.startBinding();

[java]
view plain
copy

/**
* Refreshes the shortcuts shown on the workspace.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void startBinding() {
final Workspace workspace = mWorkspace;

mWorkspace.clearDropTargets();
int count = workspace.getChildCount();
for (int i = 0; i < count; i++) {
// Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate().
final CellLayout layoutParent = (CellLayout) workspace.getChildAt(i);
layoutParent.removeAllViewsInLayout();
}
if (mHotseat != null) {
mHotseat.resetLayout();
}
}

从方法中的内容我们可以看到,当被通知开始加载Workspace中内容时,Launcher重置了Workspace中的内容,Hotseat也通过resetLayout方法进行重置。

[java]
view plain
copy

void resetLayout() {
mContent.removeAllViewsInLayout();

// Add the Apps button
Context context = getContext();
LayoutInflater inflater = LayoutInflater.from(context);
BubbleTextView allAppsButton = (BubbleTextView)
inflater.inflate(R.layout.application, mContent, false);
......

// Note: We do this to ensure that the hotseat is always laid out in the orientation of
// the hotseat in order regardless of which orientation they were added
int x = getCellXFromOrder(sAllAppsButtonRank);
int y = getCellYFromOrder(sAllAppsButtonRank);
mContent.addViewToCellLayout(allAppsButton, -1, 0, new CellLayout.LayoutParams(x,y,1,1),
true);
}

Hotseat中清空了装载的内容,然后重新加载allAppsButton。从这里也可以看到allAppsButton是固定到了Hotseat中,不同于Hotseat中的其他控件。

Step2:调用Callbacks.bindItems(ArrayList<ItemInfo> shortcuts, int start, int end)

准备工作完成之后,现在可以开始正式的加载工作了,首先被调用的是bindItems()方法

[java]
view plain
copy

/**
* Bind the items start-end from the list.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) {
setLoadOnResume();

final Workspace workspace = mWorkspace;
for (int i=start; i<end; i++) {
final ItemInfo item = shortcuts.get(i);
......
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
View shortcut = createShortcut((ShortcutInfo)item);
workspace.addInScreen(shortcut, item.container, item.screen, item.cellX,
item.cellY, 1, 1, false);
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();
}

通过这个方法,将application、shortcut、folder三种item通过Workspace.addInScreen()添加到Workspace中

[java]
view plain
copy

/**
* Adds the specified child in the specified screen. The position and dimension of
* the child are defined by x, y, spanX and spanY.
*
* @param child The child to add in one of the workspace's screens.
* @param screen The screen in which to add the child.
* @param x The X position of the child in the screen's grid.
* @param y The Y position of the child in the screen's grid.
* @param spanX The number of cells spanned horizontally by the child.
* @param spanY The number of cells spanned vertically by the child.
* @param insert When true, the child is inserted at the beginning of the children list.
*/
void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY,
boolean insert) {
......

//Workspace一共有五个分屏,每个分屏是一个CellLayout
final CellLayout layout;
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
layout = mLauncher.getHotseat().getLayout();
child.setOnKeyListener(null);

......
if (screen < 0) {
screen = mLauncher.getHotseat().getOrderInHotseat(x, y);
} else {
// Note: We do this to ensure that the hotseat is always laid out in the orientation
// of the hotseat in order regardless of which orientation they were added
//获取child的位置,返回true添加成功,false失败
x = mLauncher.getHotseat().getCellXFromOrder(screen);
y = mLauncher.getHotseat().getCellYFromOrder(screen);
}
} else {
// Show folder title if not in the hotseat
if (child instanceof FolderIcon) {
((FolderIcon) child).setTextVisible(true);
}

layout = (CellLayout) getChildAt(screen);
child.setOnKeyListener(new IconKeyEventListener());
}

CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (lp == null) {
lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
} else {
lp.cellX = x;
lp.cellY = y;
lp.cellHSpan = spanX;
lp.cellVSpan = spanY;
}

if (spanX < 0 && spanY < 0) {
lp.isLockedToGrid = false;
}

// Get the canonical child id to uniquely represent this view in this screen
int childId = LauncherModel.getCellLayoutChildId(container, screen, x, y, spanX, spanY);
boolean markCellsAsOccupied = !(child instanceof Folder);
//将child添加到CellLayout中去
if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) {
......
}

if (!(child instanceof Folder)) {
child.setHapticFeedbackEnabled(false);
child.setOnLongClickListener(mLongClickListener);
}
if (child instanceof DropTarget) {
mDragController.addDropTarget((DropTarget) child);
}
}

通过addInScreen()就能将child添加到指定的CellLayout中去。CellLayout共有六个,Workspace中五个,Hotseat一个。

Step3:调用Callbacks.bindFolders(HashMap<Long, FolderInfo> folders)

Launcher.bindFolders()中的代码只有三行:

[java]
view plain
copy

public void bindFolders(HashMap<Long, FolderInfo> folders) {
setLoadOnResume();
sFolders.clear();
sFolders.putAll(folders);
}

获取到当前的Folder的映射表。
Step4:调用Callbacks.bindAppWidgets(LauncherAppWidgetInfo item)

现在开始加载AppWidget到Workspace:

[java]
view plain
copy

/**
* Add the views for a widget to the workspace.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAppWidget(LauncherAppWidgetInfo item) {
setLoadOnResume();

......
final Workspace workspace = mWorkspace;

final int appWidgetId = item.appWidgetId;
final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
......

item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);

item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
item.hostView.setTag(item);

workspace.addInScreen(item.hostView, item.container, item.screen, item.cellX,
item.cellY, item.spanX, item.spanY, false);

addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);

workspace.requestLayout();

......
}

先获取到AppWidget的相关信息之后,调用Workspace.addInScreen()添加到Workspace。AppWidget是Android系统的一大特色,可

以在桌面上快捷的获取实时信息和对一些指定应用进行控制。AppWidget是需要自动更新的(如果应用中设置了更新),因此除了

将其添加到桌面我们需要更具需要设置自动更新。进而调用addWidgetToAutoAdvanceifNeeded()来实现此功能,关于如何实现自动

更新AppWidget的话题,本文暂不做分析。bindAppWidgets()一共被调用两次,这样做的目的是增加流畅感,第一次调用的时候为

当前显示的分屏添加AppWidget,第二次调用的时候为其他未显示的分屏添加AppWidget。这样就给用户带来了一种流畅的用户体验。

Step5:调用Callbacks.finishBindingItems()

通过上面的操作,所有item就已经悉数被添加到Workspace当中,此时调用finishBindingItems()通知Launcher添加完毕。

[java]
view plain
copy

/**
* Callback saying that there aren't any more items to bind.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
ublic void finishBindingItems() {
setLoadOnResume();
......
mWorkspaceLoading = false;

// If we received the result of any pending adds while the loader was running (e.g. the
// widget configuration forced an orientation change), process them now.
for (int i = 0; i < sPendingAddList.size(); i++) {
completeAdd(sPendingAddList.get(i));
}
sPendingAddList.clear();
......
mWorkspace.post(mBuildLayersRunnable);

当Workspace正在加载的时候,有一些操作发生却还未执行,在finishBindingItems()中来执行这些操作,调用completeAdd()来完成

还未来得及完成的操作。紧接着又向Workspace的消息队列里加入了mBuildLayersRunnable,mBuildLayersRunnable是Runnable的

一个实例,它的功能就是迫使每个View都完成渲染的工作,即及时的现实到桌面中现好了所有需要的内容了。那下一步就是需要向

All Apps页中加载内容了。

二、AllApps的内容加载

回到mLoaderTask.run()方法中,当bindWorkspace()执行结束之后,并通过waitForIdle()确认加载完成之后,就会调用

loadAndBindAllApps()来为AllApps页面加载内容。

[java]
view plain
copy

private void loadAndBindAllApps() {
......
if (!mAllAppsLoaded) {
//批量加载app和widget信息
loadAllAppsByBatch();
......
} else {
//无需重复加载,直接绑定
onlyBindAllApps();
}
}

AllApps中的加载过程和Workspace中的加载过程大致是相同的,只是All Apps的加载和绑定过程被放到同一个方法loadAllAppsByBatch()中执行:

[java]
view plain
copy

/**
*批量的向加载内容
*/
private void loadAllAppsByBatch() {
......
//设置Intent的action为ACTION_MAIN,category为CATEGORY_LAUNCHER
//这样就筛选出桌面上显示的启动项了。
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);

final PackageManager packageManager = mContext.getPackageManager();
List<ResolveInfo> apps = null;

int N = Integer.MAX_VALUE;

int startIndex;
int i=0;
int batchSize = -1;
while (i < N && !mStopped) {
if (i == 0) {
mAllAppsList.clear();
......
//查询所有应该在桌面上显示的app
apps = packageManager.queryIntentActivities(mainIntent, 0);
......

N = apps.size();
......
if (mBatchSize == 0) {
//mBatchSize==0表示一次性加载所有的应用
batchSize = N;
} else {
batchSize = mBatchSize;
}

......
//将获取到的app的信息按名字进行排序
Collections.sort(apps,
new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));
......
}

......

startIndex = i;
//添加一批应用信息到mAllAppsList,每一批添加N个
for (int j=0; i<N && j<batchSize; j++) {
// This builds the icon bitmaps.
mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i),
mIconCache, mLabelCache));
i++;
}

//i < batchSize表示添加的是第一批信息
final boolean first = i <= batchSize;
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
final ArrayList<ApplicationInfo> added = mAllAppsList.added;

//每添加完一批之后,将added重新清空
mAllAppsList.added = new ArrayList<ApplicationInfo>();

mHandler.post(new Runnable() {
public void run() {
......
//Launcher实现了Callbacks接口,将获取到的数据回调给Launcher
if (callbacks != null) {
if (first) {
callbacks.bindAllApplications(added);
} else {
callbacks.bindAppsAdded(added);
}
......
} else {
......
}
}
});
......
}
......
}

过程还是挺简单的,首先当然是查询所有的App了,通过向PackagedManager发送指定的Intent就能够获得安装好的应用的信息。查

询完毕之后,将数据封装到ArrayList<ApplicationInfo>对象中,然后通过Callbacks.bindAllApplication()或Callbacks.bindAppsAdded()

将数据传给Launcher。Launcher中的操作也比加载Workspace时简单多,毕竟这里只需要加载Icon。

[java]
view plain
copy

/**
* Add the icons for all apps.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {
......
// We just post the call to setApps so the user sees the progress bar
// disappear-- otherwise, it just looks like the progress bar froze
// which doesn't look great
mAppsCustomizeTabHost.post(new Runnable() {
public void run() {
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.setApps(apps);
}
}
});
}

[java]
view plain
copy

/**
* A package was installed.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAppsAdded(ArrayList<ApplicationInfo> apps) {
setLoadOnResume();
......

if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.addApps(apps);
}
}

这样All Apps页面的加载也完成了。

到这一步,Launcher内容的加载过程也就完成了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: