Android4.0 Launcher 源码分析2——Launcher内容加载绑定详细过程

来源:互联网 发布:商桥2016软件下载 编辑:程序博客网 时间:2024/05/18 05:03
Launcher在应用启动的时候,需要加载AppWidget,shortcut等内容项,通过调用LauncherModel.startLoader(),开始加载的工作。launcherModel中加载好的内容会通过LauncherModel.Callbacks接口的回调函数将数据传给需要的组件,那先来看看Callbacks的定义:

 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数据。

  每一部分的加载又可以分为两个步骤:

  1. 由LauncherModel完成,主要工作是从数据库中读取信息,并且按类别将内容项分装到不同的数据结构中。
  2. 由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中的数据,并将它们分类存放到指定的数据结构中。

  分类的标准有两条:

  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()中:
 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

0 0