Android4.0 Launcher 源码分析2——Launcher内容加载绑定详细过程
来源:互联网 发布:商桥2016软件下载 编辑:程序博客网 时间:2024/05/18 05:03
1 public interface Callbacks { 2 public boolean setLoadOnResume(); 3 public int getCurrentWorkspaceScreen(); 4 public void startBinding(); 5 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end); 6 public void bindFolders(HashMap<Long,FolderInfo> folders); 7 public void finishBindingItems(); 8 public void bindAppWidget(LauncherAppWidgetInfo info); 9 public void bindAllApplications(ArrayList<ApplicationInfo> apps); 10 public void bindAppsAdded(ArrayList<ApplicationInfo> apps); 11 public void bindAppsUpdated(ArrayList<ApplicationInfo> apps); 12 public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent); 13 public void bindPackagesUpdated(); 14 public boolean isAllAppsVisible(); 15 public void bindSearchablesChanged(); 16 }
简单的了解下每个方法的用途:
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()方法,开始加载内容。
1 public void startLoader(Context context, boolean isLaunching) { 2 synchronized (mLock) { 3 ...... 4 // Don't bother to start the thread if we know it's not going to do anything 5 if (mCallbacks != null && mCallbacks.get() != null) { 6 ...... 7 mLoaderTask = new LoaderTask(context, isLaunching); 8 sWorkerThread.setPriority(Thread.NORM_PRIORITY); 9 sWorker.post(mLoaderTask); 10 } 11 } 12 }
mLoaderTask是一个Runnable,被添加到消息队列之后,它的run() 方法会被调用。
1 public void run() { 2 ...... 3 keep_running: { 4 ...... 5 if (loadWorkspaceFirst) { 6 ...... 7 loadAndBindWorkspace(); 8 } else { 9 ...... 10 } 11 12 if (mStopped) { 13 break keep_running; 14 } 15 16 ...... 17 waitForIdle(); 18 19 // second step 20 if (loadWorkspaceFirst) { 21 ...... 22 loadAndBindAllApps(); 23 } else { 24 ...... 25 } 26 ...... 27 } 28 ...... 29 }
加载的工作由两部分组成,第一部分是为Workspace加载内容,第二部分则是为AllApps加载内容。原则:如果AllApps在前台,则优先加载AllApps,否则,加载WorkSpace数据。
每一部分的加载又可以分为两个步骤:
- 由LauncherModel完成,主要工作是从数据库中读取信息,并且按类别将内容项分装到不同的数据结构中。
- 由Launcher来完成,通过LauncherModel.Callbacks接口定义的回调方法,从LauncherModel中获取的数据,将其显示到桌面。
1、Workspace
1.1 内容加载:loadAndBindWorkspace() -> loadWorkspace() -> bindWorkspace()
run()中首先会调用loadAndBindWorkspace()方法开始Workspace的加载工作。
1 private void loadAndBindWorkspace() { 2 ... 3 if (!mWorkspaceLoaded) { 4 loadWorkspace(); 5 synchronized (LoaderTask.this) { 6 if (mStopped) { 7 return; 8 } 9 mWorkspaceLoaded = true; 10 } 11 } 12 13 // Bind the workspace 14 bindWorkspace(); 15 }
因为WorkspaceLoaded=false,所以会调用loadWorkspace()读取内容数据,等数据读取完毕之后,再调用bindWorkspace()将数据加载到Workspace中。
1 private void loadWorkspace() { 2 ...... 3 4 //存放container为CONTAINER_DESKTOP和CONTAINER_HOTSEAT类型的item 5 sWorkspaceItems.clear(); 6 7 //存放所有的AppWidget类型 8 sAppWidgets.clear(); 9 10 //存放的FolderInfo.id和FolderInfo组成的映射对 11 sFolders.clear(); 12 13 //所有的item的id和ItemInfo组成的映射对 14 sItemsIdMap.clear(); 15 sDbIconCache.clear(); 16 17 final ArrayList<Long> itemsToRemove = new ArrayList<Long>(); 18 19 final Cursor c = contentResolver.query( 20 LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); 21 22 // +1 for the hotseat (it can be larger than the workspace) 23 // Load workspace in reverse order to ensure that latest items are loaded first (and 24 // before any earlier duplicates) 25 //代表屏幕中的每一个单位的方格是否被占用。 26 //第一维表示分屏的序号,其中最后一个代表Hotseat 27 //第二维表示x方向方格的序号 28 //第三维表示y方向方格的序号 29 final ItemInfo occupied[][][] = 30 new ItemInfo[Launcher.SCREEN_COUNT + 1][mCellCountX + 1][mCellCountY + 1]; 31 32 try { 33 ...... 34 35 while (!mStopped && c.moveToNext()) { 36 try { 37 int itemType = c.getInt(itemTypeIndex); 38 39 switch (itemType) { 40 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 41 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 42 intentDescription = c.getString(intentIndex); 43 try { 44 intent = Intent.parseUri(intentDescription, 0); 45 } catch (URISyntaxException e) { 46 continue; 47 } 48 49 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) { 50 info = getShortcutInfo(manager, intent, context, c, iconIndex, 51 titleIndex, mLabelCache); 52 } else { 53 info = getShortcutInfo(c, context, iconTypeIndex, 54 iconPackageIndex, iconResourceIndex, iconIndex, 55 titleIndex); 56 } 57 58 if (info != null) { 59 ...... 60 61 // check & update map of what's occupied 62 //检查这个item所占的空间是否空闲,true表示空闲 63 if (!checkItemPlacement(occupied, info)) { 64 break; 65 } 66 67 switch (container) { 68 case LauncherSettings.Favorites.CONTAINER_DESKTOP: 69 case LauncherSettings.Favorites.CONTAINER_HOTSEAT: 70 //当加载的item类型为ITEM_TYPE_APPLICATION或者ITEM_TYPE_SHORTCUT 71 //并且所属的container为CONTAINER_DESKTOP或者CONTAINER_HOTSEAT时 72 //将其添加到sWorkspaceItems中 73 sWorkspaceItems.add(info); 74 break; 75 default: 76 // Item is in a user folder 77 //如果item的container不是上述两者,则代表它处于一个folder中 78 //将其添加到所属的folderInfo中 79 FolderInfo folderInfo = 80 findOrMakeFolder(sFolders, container); 81 folderInfo.add(info); 82 break; 83 } 84 //所有的ITEM_TYPE_APPLICATION和ITEM_TYPE_SHORTCUT类型的item都需要 85 //加入到sItemsIdMap的映射对中。 86 sItemsIdMap.put(info.id, info); 87 88 // now that we've loaded everthing re-save it with the 89 // icon in case it disappears somehow. 90 queueIconToBeChecked(sDbIconCache, info, c, iconIndex); 91 } else { 92 // Failed to load the shortcut, probably because the 93 // activity manager couldn't resolve it (maybe the app 94 // was uninstalled), or the db row was somehow screwed up. 95 // Delete it. 96 id = c.getLong(idIndex); 97 Log.e(TAG, "Error loading shortcut " + id + ", removing it"); 98 contentResolver.delete(LauncherSettings.Favorites.getContentUri( 99 id, false), null, null); 100 } 101 break; 102 103 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 104 id = c.getLong(idIndex); 105 FolderInfo folderInfo = findOrMakeFolder(sFolders, id); 106 ..... 107 // check & update map of what's occupied 108 if (!checkItemPlacement(occupied, folderInfo)) { 109 break; 110 } 111 switch (container) { 112 case LauncherSettings.Favorites.CONTAINER_DESKTOP: 113 case LauncherSettings.Favorites.CONTAINER_HOTSEAT: 114 //folderInfo类型的item也需要添加到sWorkspaceItems中 115 sWorkspaceItems.add(folderInfo); 116 break; 117 } 118 //添加到sItemsIdMap映射对中 119 sItemsIdMap.put(folderInfo.id, folderInfo); 120 //添加到sFolder映射对中 121 sFolders.put(folderInfo.id, folderInfo); 122 break; 123 124 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: 125 // Read all Launcher-specific widget details 126 int appWidgetId = c.getInt(appWidgetIdIndex); 127 id = c.getLong(idIndex); 128 129 final AppWidgetProviderInfo provider = 130 widgets.getAppWidgetInfo(appWidgetId); 131 132 if (!isSafeMode && (provider == null || provider.provider == null || 133 provider.provider.getPackageName() == null)) { 134 ...... 135 itemsToRemove.add(id); 136 } else { 137 appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId); 138 ...... 139 140 container = c.getInt(containerIndex); 141 ...... 142 appWidgetInfo.container = c.getInt(containerIndex); 143 144 // check & update map of what's occupied 145 if (!checkItemPlacement(occupied, appWidgetInfo)) { 146 break; 147 } 148 //添加到sItemsIdMap映射对 149 sItemsIdMap.put(appWidgetInfo.id, appWidgetInfo); 150 //添加到sAppWidgets 151 sAppWidgets.add(appWidgetInfo); 152 } 153 break; 154 } 155 } catch (Exception e) { 156 ...... 157 } 158 } 159 } finally { 160 c.close(); 161 } 162 163 ...... 164 }
loadWorkspace的工作就是从ContentProvider获取指定URI中的数据,并将它们分类存放到指定的数据结构中。
分类的标准有两条:
- item的类型。包括ITEM_TYPE_APPLICATION ,ITEM_TYPE_SHORTCUT ,ITEM_TYPE_FOLDER,ITEM_TYPE_APPWIDGET四类。
- item所属的容器。包括CONTAINER_DESKTOP,CONTAINER_HOTSEAT以及其它(主要指文件夹)。LauncherModel在读取完数据之后,通过LauncherModel.bindWorkspace()将数据传给到Launcher。进入LauncherModel.bindWorkspace()中:
1 private void bindWorkspace() { 2 ...... 3 mHandler.post(new Runnable() { 4 public void run() { 5 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 6 if (callbacks != null) { 7 //开始绑定 8 callbacks.startBinding(); 9 } 10 } 11 }); 12 13 ...... 14 for (int i=0; i<N; i+=ITEMS_CHUNK) { 15 final int start = i; 16 final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i); 17 mHandler.post(new Runnable() { 18 public void run() { 19 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 20 if (callbacks != null) { 21 //绑定application、shortcut、folder三种内容 22 callbacks.bindItems(workspaceItems, start, start+chunkSize); 23 } 24 } 25 }); 26 } 27 ...... 28 mHandler.post(new Runnable() { 29 public void run() { 30 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 31 if (callbacks != null) { 32 //绑定folder 33 callbacks.bindFolders(folders); 34 } 35 } 36 }); 37 ...... 38 for (int i=0; i<N; i++) { 39 final LauncherAppWidgetInfo widget = sAppWidgets.get(i); 40 if (widget.screen == currentScreen) { 41 mHandler.post(new Runnable() { 42 public void run() { 43 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 44 if (callbacks != null) { 45 //绑定当前屏的AppWidget 46 callbacks.bindAppWidget(widget); 47 } 48 } 49 }); 50 } 51 } 52 53 for (int i=0; i<N; i++) { 54 final LauncherAppWidgetInfo widget = sAppWidgets.get(i); 55 if (widget.screen != currentScreen) { 56 mHandler.post(new Runnable() { 57 public void run() { 58 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 59 if (callbacks != null) { 60 //绑定其它屏的AppWidget 61 callbacks.bindAppWidget(widget); 62 } 63 } 64 }); 65 } 66 } 67 68 mHandler.post(new Runnable() { 69 public void run() { 70 Callbacks callbacks = tryGetCallbacks(oldCallbacks); 71 if (callbacks != null) { 72 //结束绑定 73 callbacks.finishBindingItems(); 74 } 75 } 76 }); 77 ...... 78 }
1.2 内容绑定
可以看到,Launcher的内容绑定分为五步:分别对应着startBinding()、bindItems()、bindFolders()、bindAppWidgets()、finishBindingItems()的调用
Step1:调用Callbacks.startBinding()
由于Launcher实现了Callbacks接口,Launcher中的startBinding()被调用,进入Launcher.startBinding();
1 /** 2 * Refreshes the shortcuts shown on the workspace. 3 * 4 * Implementation of the method from LauncherModel.Callbacks. 5 */ 6 public void startBinding() { 7 final Workspace workspace = mWorkspace; 8 9 mWorkspace.clearDropTargets(); 10 int count = workspace.getChildCount(); 11 for (int i = 0; i < count; i++) { 12 // Use removeAllViewsInLayout() to avoid an extra requestLayout() and invalidate(). 13 final CellLayout layoutParent = (CellLayout) workspace.getChildAt(i); 14 layoutParent.removeAllViewsInLayout(); 15 } 16 if (mHotseat != null) { 17 mHotseat.resetLayout(); 18 } 19 }
从方法中的内容我们可以看到,当被通知开始加载Workspace中内容时,Launcher重置了Workspace中的内容,Hotseat也通过resetLayout方法进行重置。
1 void resetLayout() { 2 mContent.removeAllViewsInLayout(); 3 4 // Add the Apps button 5 Context context = getContext(); 6 LayoutInflater inflater = LayoutInflater.from(context); 7 BubbleTextView allAppsButton = (BubbleTextView) 8 inflater.inflate(R.layout.application, mContent, false); 9 ...... 10 11 // Note: We do this to ensure that the hotseat is always laid out in the orientation of 12 // the hotseat in order regardless of which orientation they were added 13 int x = getCellXFromOrder(sAllAppsButtonRank); 14 int y = getCellYFromOrder(sAllAppsButtonRank); 15 mContent.addViewToCellLayout(allAppsButton, -1, 0, new CellLayout.LayoutParams(x,y,1,1), 16 true); 17 }
Hotseat中清空了装载的内容,然后重新加载allAppsButton。从这里也可以看到allAppsButton是固定到了Hotseat中,不同于Hotseat中的其他控件。
Step2:调用Callbacks.bindItems(ArrayList<ItemInfo> shortcuts, int start, int end)
准备工作完成之后,现在可以开始正式的加载工作了,首先被调用的是bindItems()方法
1 /** 2 * Bind the items start-end from the list. 3 * 4 * Implementation of the method from LauncherModel.Callbacks. 5 */ 6 public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) { 7 setLoadOnResume(); 8 9 final Workspace workspace = mWorkspace; 10 for (int i=start; i<end; i++) { 11 final ItemInfo item = shortcuts.get(i); 12 ...... 13 switch (item.itemType) { 14 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: 15 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: 16 View shortcut = createShortcut((ShortcutInfo)item); 17 workspace.addInScreen(shortcut, item.container, item.screen, item.cellX, 18 item.cellY, 1, 1, false); 19 break; 20 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: 21 FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, 22 (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), 23 (FolderInfo) item, mIconCache); 24 workspace.addInScreen(newFolder, item.container, item.screen, item.cellX, 25 item.cellY, 1, 1, false); 26 break; 27 } 28 } 29 workspace.requestLayout(); 30 }
通过这个方法,将application、shortcut、folder三种item通过Workspace.addInScreen()添加到Workspace中
1 /** 2 * Adds the specified child in the specified screen. The position and dimension of 3 * the child are defined by x, y, spanX and spanY. 4 * 5 * @param child The child to add in one of the workspace's screens. 6 * @param screen The screen in which to add the child. 7 * @param x The X position of the child in the screen's grid. 8 * @param y The Y position of the child in the screen's grid. 9 * @param spanX The number of cells spanned horizontally by the child. 10 * @param spanY The number of cells spanned vertically by the child. 11 * @param insert When true, the child is inserted at the beginning of the children list. 12 */ 13 void addInScreen(View child, long container, int screen, int x, int y, int spanX, int spanY, 14 boolean insert) { 15 ...... 16 17 //Workspace一共有五个分屏,每个分屏是一个CellLayout 18 final CellLayout layout; 19 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 20 layout = mLauncher.getHotseat().getLayout(); 21 child.setOnKeyListener(null); 22 23 ...... 24 if (screen < 0) { 25 screen = mLauncher.getHotseat().getOrderInHotseat(x, y); 26 } else { 27 // Note: We do this to ensure that the hotseat is always laid out in the orientation 28 // of the hotseat in order regardless of which orientation they were added 29 //获取child的位置,返回true添加成功,false失败 30 x = mLauncher.getHotseat().getCellXFromOrder(screen); 31 y = mLauncher.getHotseat().getCellYFromOrder(screen); 32 } 33 } else { 34 // Show folder title if not in the hotseat 35 if (child instanceof FolderIcon) { 36 ((FolderIcon) child).setTextVisible(true); 37 } 38 39 layout = (CellLayout) getChildAt(screen); 40 child.setOnKeyListener(new IconKeyEventListener()); 41 } 42 43 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); 44 if (lp == null) { 45 lp = new CellLayout.LayoutParams(x, y, spanX, spanY); 46 } else { 47 lp.cellX = x; 48 lp.cellY = y; 49 lp.cellHSpan = spanX; 50 lp.cellVSpan = spanY; 51 } 52 53 if (spanX < 0 && spanY < 0) { 54 lp.isLockedToGrid = false; 55 } 56 57 // Get the canonical child id to uniquely represent this view in this screen 58 int childId = LauncherModel.getCellLayoutChildId(container, screen, x, y, spanX, spanY); 59 boolean markCellsAsOccupied = !(child instanceof Folder); 60 //将child添加到CellLayout中去 61 if (!layout.addViewToCellLayout(child, insert ? 0 : -1, childId, lp, markCellsAsOccupied)) { 62 ...... 63 } 64 65 if (!(child instanceof Folder)) { 66 child.setHapticFeedbackEnabled(false); 67 child.setOnLongClickListener(mLongClickListener); 68 } 69 if (child instanceof DropTarget) { 70 mDragController.addDropTarget((DropTarget) child); 71 } 72 }
通过addInScreen()就能将child添加到指定的CellLayout中去。CellLayout共有六个,Workspace中五个,Hotseat一个。
Step3:调用Callbacks.bindFolders(HashMap<Long, FolderInfo> folders)
Launcher.bindFolders()中的代码只有三行:
1 public void bindFolders(HashMap<Long, FolderInfo> folders) { 2 setLoadOnResume(); 3 sFolders.clear(); 4 sFolders.putAll(folders); 5 }
获取到当前的Folder的映射表。
Step4:调用Callbacks.bindAppWidgets(LauncherAppWidgetInfo item)
现在开始加载AppWidget到Workspace:
1 /** 2 * Add the views for a widget to the workspace. 3 * 4 * Implementation of the method from LauncherModel.Callbacks. 5 */ 6 public void bindAppWidget(LauncherAppWidgetInfo item) { 7 setLoadOnResume(); 8 9 ...... 10 final Workspace workspace = mWorkspace; 11 12 final int appWidgetId = item.appWidgetId; 13 final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 14 ...... 15 16 item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); 17 18 item.hostView.setAppWidget(appWidgetId, appWidgetInfo); 19 item.hostView.setTag(item); 20 21 workspace.addInScreen(item.hostView, item.container, item.screen, item.cellX, 22 item.cellY, item.spanX, item.spanY, false); 23 24 addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo); 25 26 workspace.requestLayout(); 27 28 ...... 29 }
先获取到AppWidget的相关信息之后,调用Workspace.addInScreen()添加到Workspace。AppWidget是Android系统的一大特色,可以在桌面上快捷的获取实时信息和对一些指定应用进行控制。AppWidget是需要自动更新的(如果应用中设置了更新),因此除了将其添加到桌面我们需要更具需要设置自动更新。进而调用addWidgetToAutoAdvanceifNeeded()来实现此功能,关于如何实现自动更新AppWidget的话题,本文暂不做分析。bindAppWidgets()一共被调用两次,这样做的目的是增加流畅感,第一次调用的时候为当前显示的分屏添加AppWidget,第二次调用的时候为其他未显示的分屏添加AppWidget。这样就给用户带来了一种流畅的用户体验。
Step5:调用Callbacks.finishBindingItems()
通过上面的操作,所有item就已经悉数被添加到Workspace当中,此时调用finishBindingItems()通知Launcher添加完毕。
1 /** 2 * Callback saying that there aren't any more items to bind. 3 * 4 * Implementation of the method from LauncherModel.Callbacks. 5 */ 6 ublic void finishBindingItems() { 7 setLoadOnResume(); 8 ...... 9 mWorkspaceLoading = false; 10 11 // If we received the result of any pending adds while the loader was running (e.g. the 12 // widget configuration forced an orientation change), process them now. 13 for (int i = 0; i < sPendingAddList.size(); i++) { 14 completeAdd(sPendingAddList.get(i)); 15 } 16 sPendingAddList.clear(); 17 ...... 18 mWorkspace.post(mBuildLayersRunnable);
当Workspace正在加载的时候,有一些操作发生却还未执行,在finishBindingItems()中来执行这些操作,调用completeAdd()来完成还未来得及完成的操作。紧接着又向Workspace的消息队列里加入了mBuildLayersRunnable,mBuildLayersRunnable是Runnable的一个实例,它的功能就是迫使每个View都完成渲染的工作,即及时的现实到桌面中现好了所有需要的内容了。那下一步就是需要向All Apps页中加载内容了。
2、AllApps的内容加载:loadAndBindAllApps() -> loadAllAppsByBatch()/onlyBindAllApps() -> bindAllApplication()/bindAppsAdded()
回到mLoaderTask.run()方法中,当bindWorkspace()执行结束之后,并通过waitForIdle()确认加载完成之后,就会调用loadAndBindAllApps()来为AllApps页面加载内容。
1 private void loadAndBindAllApps() { 2 ...... 3 if (!mAllAppsLoaded) { 4 //批量加载app和widget信息 5 loadAllAppsByBatch(); 6 ...... 7 } else { 8 //无需重复加载,直接绑定 9 onlyBindAllApps(); 10 } 11 }
AllApps中的加载过程和Workspace中的加载过程大致是相同的,只是All Apps的加载和绑定过程被放到同一个方法loadAllAppsByBatch()中执行:
1 /** 2 *批量的向加载内容 3 */ 4 private void loadAllAppsByBatch() { 5 ...... 6 //设置Intent的action为ACTION_MAIN,category为CATEGORY_LAUNCHER 7 //这样就筛选出桌面上显示的启动项了。 8 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 9 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 10 11 final PackageManager packageManager = mContext.getPackageManager(); 12 List<ResolveInfo> apps = null; 13 14 int N = Integer.MAX_VALUE; 15 16 int startIndex; 17 int i=0; 18 int batchSize = -1; 19 while (i < N && !mStopped) { 20 if (i == 0) { 21 mAllAppsList.clear(); 22 ...... 23 //查询所有应该在桌面上显示的app 24 apps = packageManager.queryIntentActivities(mainIntent, 0); 25 ...... 26 27 N = apps.size(); 28 ...... 29 if (mBatchSize == 0) { 30 //mBatchSize==0表示一次性加载所有的应用 31 batchSize = N; 32 } else { 33 batchSize = mBatchSize; 34 } 35 36 ...... 37 //将获取到的app的信息按名字进行排序 38 Collections.sort(apps, 39 new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache)); 40 ...... 41 } 42 43 ...... 44 45 startIndex = i; 46 //添加一批应用信息到mAllAppsList,每一批添加N个 47 for (int j=0; i<N && j<batchSize; j++) { 48 // This builds the icon bitmaps. 49 mAllAppsList.add(new ApplicationInfo(packageManager, apps.get(i), 50 mIconCache, mLabelCache)); 51 i++; 52 } 53 54 //i < batchSize表示添加的是第一批信息 55 final boolean first = i <= batchSize; 56 final Callbacks callbacks = tryGetCallbacks(oldCallbacks); 57 final ArrayList<ApplicationInfo> added = mAllAppsList.added; 58 59 //每添加完一批之后,将added重新清空 60 mAllAppsList.added = new ArrayList<ApplicationInfo>(); 61 62 mHandler.post(new Runnable() { 63 public void run() { 64 ...... 65 //Launcher实现了Callbacks接口,将获取到的数据回调给Launcher 66 if (callbacks != null) { 67 if (first) { 68 callbacks.bindAllApplications(added); 69 } else { 70 callbacks.bindAppsAdded(added); 71 } 72 ...... 73 } else { 74 ...... 75 } 76 } 77 }); 78 ...... 79 } 80 ...... 81 }
过程还是挺简单的,首先当然是查询所有的App了,通过向PackagedManager发送指定的Intent就能够获得安装好的应用的信息。查询完毕之后,将数据封装到ArrayList<ApplicationInfo>对象中,然后通过Callbacks.bindAllApplication()或Callbacks.bindAppsAdded()将数据传给Launcher。Launcher中的操作也比加载Workspace时简单多,毕竟这里只需要加载Icon。
1 /** 2 * Add the icons for all apps. 3 * 4 * Implementation of the method from LauncherModel.Callbacks. 5 */ 6 public void bindAllApplications(final ArrayList<ApplicationInfo> apps) { 7 ...... 8 // We just post the call to setApps so the user sees the progress bar 9 // disappear-- otherwise, it just looks like the progress bar froze 10 // which doesn't look great 11 mAppsCustomizeTabHost.post(new Runnable() { 12 public void run() { 13 if (mAppsCustomizeContent != null) { 14 mAppsCustomizeContent.setApps(apps); 15 } 16 } 17 }); 18 }
1 /** 2 * A package was installed. 3 * 4 * Implementation of the method from LauncherModel.Callbacks. 5 */ 6 public void bindAppsAdded(ArrayList<ApplicationInfo> apps) { 7 setLoadOnResume(); 8 ...... 9 10 if (mAppsCustomizeContent != null) { 11 mAppsCustomizeContent.addApps(apps); 12 } 13 }
这样All Apps页面的加载也完成了。
到这一步,Launcher内容的加载过程也就完成了。
http://blog.csdn.net/chenshaoyang0011/article/details/7831617
- Android4.0 Launcher 源码分析2——Launcher内容加载绑定详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- 【转载】Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android 4.0 Launcher2源码分析——Launcher内容加载详细过程
- Android4.0 Launcher 源码分析1——Launcher整体结构
- Android4.0 Launcher源码分析系列(四)
- Android4.0 Launcher 源码分析系列
- Android4.0 Launcher 源码分析系列
- Android4.0源码Launcher启动流程分析
- java设计模式——策略模式
- 《Android应用性能优化》3——电量、渲染
- 《Android应用性能优化》2——内存、CPU、性能测评
- RHEL7.2 安装Texlive2016时出现Can't locate Digest/MD5.pm的解决方法
- 《Android应用性能优化》1——代码
- Android4.0 Launcher 源码分析2——Launcher内容加载绑定详细过程
- Android4.0 Launcher 源码分析1——Launcher整体结构
- Android4.0 Launcher 源码分析3——WorkSpace结构(滑动)
- java 蓝桥杯 奇怪的比赛
- Android4.0 Launcher拖拽原理分析
- ffwt进行fft
- Android启动流程
- Android系统架构
- java内存模型