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

来源:互联网 发布:微秀制作软件 编辑:程序博客网 时间:2024/05/21 15:07

本文来自http://blog.csdn.net/chenshaoyang0011 转载请申明文章出处!

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

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

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

[java] view plaincopyprint?
  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()方法,开始加载内容。

[java] view plaincopyprint?
  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() 方法会被调用。
[java] view plaincopyprint?
  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加载内容。每一部分的加载又可以分为两个步骤:1、由LauncherModel完成,主要

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

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

一、Workspace内容加载

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

[java] view plaincopyprint?
  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中。

[java] view plaincopyprint?
  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, nullnullnullnull);  
  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), nullnull);  
  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()中:

[java] view plaincopyprint?
  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.        }  

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

Step1:调用Callbacks.startBinding()

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

[java] view plaincopyprint?
  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方法进行重置。

[java] view plaincopyprint?
  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, -10new 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()方法

[java] view plaincopyprint?
  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, 11false);  
  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, 11false);  
  26.                 break;  
  27.         }  
  28.     }  
  29.     workspace.requestLayout();  
  30. }  
通过这个方法,将application、shortcut、folder三种item通过Workspace.addInScreen()添加到Workspace中
[java] view plaincopyprint?
  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()中的代码只有三行:

[java] view plaincopyprint?
  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:

[java] view plaincopyprint?
  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添加完毕。

[java] view plaincopyprint?
  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页中加载内容了。

二、AllApps的内容加载

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

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

[java] view plaincopyprint?
  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()中执行:

[java] view plaincopyprint?
  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。

[java] view plaincopyprint?
  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.  }  


[java] view plaincopyprint?
  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内容的加载过程也就完成了。


原创粉丝点击