zoukankan      html  css  js  c++  java
  • launcher4.0加载

    摘自http://blog.csdn.net/chenshaoyang0011

    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.     }  
    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()方法,开始加载内容。

    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.     }  
    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() 方法会被调用。

    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.         }  
    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的加载工作。

    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.         }  
    private void loadAndBindWorkspace() {
                ...
                if (!mWorkspaceLoaded) {
                    loadWorkspace();
                    synchronized (LoaderTask.this) {
                        if (mStopped) {
                            return;
                        }
                        mWorkspaceLoaded = true;
                    }
                }
    
                // Bind the workspace
                bindWorkspace();
            }

    因为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, 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.         }  
    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()中:

    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.        }  
     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();

    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. }  
        /**
         * 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方法进行重置。

    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. }  
        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()方法

    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. }  
         /**
         * 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中

    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. }  
        /**
         * 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()中的代码只有三行:

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

    获取到当前的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. }  
         /**
         * 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添加完毕。

    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);  
         /**
         * Callback saying that there aren't any more items to bind.
         *
         * Implementation of the method from LauncherModel.Callbacks.
         */
        public 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页面加载内容。

    1. private void loadAndBindAllApps() {  
    2.             ......  
    3.             if (!mAllAppsLoaded) {  
    4.                 //批量加载app和widget信息   
    5.                 loadAllAppsByBatch();  
    6.                 ......  
    7.             } else {  
    8.                 //无需重复加载,直接绑定   
    9.                 onlyBindAllApps();  
    10.             }  
    11.         }  
    private void loadAndBindAllApps() {
                ......
                if (!mAllAppsLoaded) {
                    //批量加载app和widget信息
                    loadAllAppsByBatch();
                    ......
                } else {
                    //无需重复加载,直接绑定
                    onlyBindAllApps();
                }
            }

    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.  }  
           /**
            *批量的向加载内容
            */
            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。

    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.  }  
       /**
         * 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);
                    }
                }
            });
        }

     

    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. }  
        /**
         * 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内容的加载过程也就完成了。

  • 相关阅读:
    spring相关记录
    xshell不能连接VM中的ubuntu
    MySQL 获得当前日期时间(以及时间的转换)
    struts2 action获取ajax提交数据中文乱码问题
    Write operations are not allowed in read-only mode (FlushMode.NEVER/
    在Action中以Struts2的方式输出JSON数据
    javascript 对象数组排序
    期待2015
    Mysql 导出数据库和指定表中的数据
    Ajax跨域问题
  • 原文地址:https://www.cnblogs.com/lyz459/p/2807495.html
Copyright © 2011-2022 走看看