《Android进阶》之第二篇 launcher

 1 public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params,
 2             boolean markCells) {
 3         final LayoutParams lp = params;
 4 
 5         // Hotseat icons - remove text
 6         if (child instanceof BubbleTextView) {
 7             BubbleTextView bubbleChild = (BubbleTextView) child;
 8             bubbleChild.setTextVisibility(!mIsHotseat);
 9         }
10 
11         child.setScaleX(getChildrenScale());
12         child.setScaleY(getChildrenScale());
13 
14         // Generate an id for each view, this assumes we have at most 256x256 cells
15         // per workspace screen
16         if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
17             // If the horizontal or vertical span is set to -1, it is taken to
18             // mean that it spans the extent of the CellLayout
19             if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
20             if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
21 
22             child.setId(childId);
23 
24             mShortcutsAndWidgets.addView(child, index, lp);
25 
26             if (markCells) markCellsAsOccupiedForView(child);
27 
28             return true;
29         }
30         return false;
31     }

allapp这就是加载每个icon到view的那个位置

1、将就的地方 launcher.java

  static int[] getSpanForWidget(Context context, ComponentName component, int minWidth,
            int minHeight) {
//        Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context, component, null);
      
        Rect padding = new Rect(20, 20, 300, 300);
        // We want to account for the extra amount of padding that we are adding to the widget
        // to ensure that it gets the full amount of space that it has requested
        int requiredWidth = minWidth + padding.left + padding.right;
        int requiredHeight = minHeight + padding.top + padding.bottom;
        return CellLayout.rectToCell(requiredWidth, requiredHeight, null);
    }
View Code

2、launcher.java

 1 /**
 2      * Add the icons for all apps.
 3      *
 4      * Implementation of the method from LauncherModel.Callbacks.
 5      */
 6     public void bindAllApplications(final ArrayList<AppInfo> apps) {
 7         if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
 8             if (mIntentsOnWorkspaceFromUpgradePath != null) {
 9                 if (LauncherModel.UPGRADE_USE_MORE_APPS_FOLDER) {
10                     getHotseat().addAllAppsFolder(mIconCache, apps,
11                             mIntentsOnWorkspaceFromUpgradePath, Launcher.this, mWorkspace);
12                 }
13                 mIntentsOnWorkspaceFromUpgradePath = null;
14             }
15         } else {
16             if (mAppsCustomizeContent != null) {
17                 mAppsCustomizeContent.setApps(apps);
18             }
19         }
20     }
View Code

3、判断是否在桌面

public boolean isAllAppsVisible() {
  return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
}

1、Callbacks接口

LauncherModel里面,需要先分析一个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                               boolean forceAnimateIcons);
 7         public void bindScreens(ArrayList<Long> orderedScreenIds);
 8         public void bindAddScreens(ArrayList<Long> orderedScreenIds);
 9         public void bindFolders(HashMap<Long,FolderInfo> folders);
10         public void finishBindingItems(boolean upgradePath);
11         public void bindAppWidget(LauncherAppWidgetInfo info);
12         public void bindAllApplications(ArrayList<AppInfo> apps);
13         public void bindAppsAdded(ArrayList<Long> newScreens,
14                                   ArrayList<ItemInfo> addNotAnimated,
15                                   ArrayList<ItemInfo> addAnimated,
16                                   ArrayList<AppInfo> addedApps);
17         public void bindAppsUpdated(ArrayList<AppInfo> apps);
18         public void bindComponentsRemoved(ArrayList<String> packageNames,
19                         ArrayList<AppInfo> appInfos,
20                         boolean matchPackageNamesOnly);
21         public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
22         public void bindSearchablesChanged();
23         public boolean isAllAppsButtonRank(int rank);
24         public void onPageBoundSynchronously(int page);
25         public void dumpLogsToLocalData();
26     }

Callbacks接口提供了很多接口,用于返回相关的数据给Launcher模块,下面我们对每个接口作用做个阐释。

setLoadOnResume() :当Launcher.java类的Activity处于onPause的时候,如果重新恢复,需要调用onResume,此时需要在onResume调用这个接口,恢复Launcher数据。

getCurrentWorkspace():获取屏幕序号(0~4)

startBinding():通知Launcher开始加载数据。清空容器数据,重新加载

bindItems(ArrayList<ItemInfo> shortcuts, int start, int end):加载App shortcut、Live Folder、widget到Launcher相关容器。

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

finishBindingItems():数据加载完成。

bindAppWidget(LauncherAppWidgetInfo item):workspace加载APP 快捷方式

bindAllApplications(final ArrayList<ApplicationInfo> apps):所有应用列表接着APP图标数据

bindAppsAdded(ArrayList<ApplicationInfo> apps):通知Launcher新安装了一个APP,更新数据。

bindAppsUpdated(ArrayList<ApplicationInfo> apps):通知Launcher一个APP更新了。(覆盖安装)

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

bindPackagesUpdated():多个应用更新。

isAllAppsVisible():返回所有应用列表是否可见状态。

bindSearchablesChanged():Google搜索栏或者删除区域发生变化时通知Launcher

2、数据加载流程

Launcher.java类继承了Callbacks接口,并实现了该接口。LauncherModel里面会调用这些接口,反馈数据和状态给Launcher。数据加载总体分为两部分,一部分是加载workspace的数据,另一部分是加载All APP界面的数据。

3、startLoader()

下面我们先分析startLoader()接口,startLoader主要是启动了一个线程,用于加载数据。

 1  public void startLoader(boolean isLaunching, int synchronousBindPage) {
 2         synchronized (mLock) {
 3             if (DEBUG_LOADERS) {
 4                 Log.d(TAG, "startLoader isLaunching=" + isLaunching);
 5             }
 6 
 7             // Clear any deferred bind-runnables from the synchronized load process
 8             // We must do this before any loading/binding is scheduled below.
 9             mDeferredBindRunnables.clear();
10 
11             // Don't bother to start the thread if we know it's not going to do anything
12             if (mCallbacks != null && mCallbacks.get() != null) {
13                 // If there is already one running, tell it to stop.
14                 // also, don't downgrade isLaunching if we're already running
15                 isLaunching = isLaunching || stopLoaderLocked();
16                 mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching);
17                 if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
18                     mLoaderTask.runBindSynchronousPage(synchronousBindPage);
19                 } else {
20                     sWorkerThread.setPriority(Thread.NORM_PRIORITY);
21                     sWorker.post(mLoaderTask);
22                 }
23             }
24         }
25     }

 4、LoaderTask的run()方法

 1  public void run() {
 2             boolean isUpgrade = false;
 3 
 4             synchronized (mLock) {
 5                 mIsLoaderTaskRunning = true;
 6             }
 7             // Optimize for end-user experience: if the Launcher is up and // running with the
 8             // All Apps interface in the foreground, load All Apps first. Otherwise, load the
 9             // workspace first (default).
10             keep_running: {
11                 // Elevate priority when Home launches for the first time to avoid
12                 // starving at boot time. Staring at a blank home is not cool.
13                 synchronized (mLock) {
14                     if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
15                             (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
16                     android.os.Process.setThreadPriority(mIsLaunching
17                             ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
18                 }
19                 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
20                 isUpgrade = loadAndBindWorkspace();
21 
22                 if (mStopped) {
23                     break keep_running;
24                 }
25 
26                 // Whew! Hard work done.  Slow us down, and wait until the UI thread has
27                 // settled down.
28                 synchronized (mLock) {
29                     if (mIsLaunching) {
30                         if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
31                         android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
32                     }
33                 }
34                 waitForIdle();
35 
36                 // second step
37                 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
38                 loadAndBindAllApps();
39 
40                 // Restore the default thread priority after we are done loading items
41                 synchronized (mLock) {
42                     android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
43                 }
44             }
45 
46             // Update the saved icons if necessary
47             if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
48             synchronized (sBgLock) {
49                 for (Object key : sBgDbIconCache.keySet()) {
50                     updateSavedIcon(mContext, (ShortcutInfo) key, sBgDbIconCache.get(key));
51                 }
52                 sBgDbIconCache.clear();
53             }
54 
55             if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
56                 // Ensure that all the applications that are in the system are
57                 // represented on the home screen.
58                 if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
59                     verifyApplications();
60                 }
61             }
62 
63             // Clear out this reference, otherwise we end up holding it until all of the
64             // callback runnables are done.
65             mContext = null;
66 
67             synchronized (mLock) {
68                 // If we are still the last one to be scheduled, remove ourselves.
69                 if (mLoaderTask == this) {
70                     mLoaderTask = null;
71                 }
72                 mIsLoaderTaskRunning = false;
73             }
74         }
View Code
 1 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
 2                 isUpgrade = loadAndBindWorkspace();
 3 
 4                 if (mStopped) {
 5                     break keep_running;
 6                 }
 7 
 8                 // Whew! Hard work done.  Slow us down, and wait until the UI thread has
 9                 // settled down.
10                 synchronized (mLock) {
11                     if (mIsLaunching) {
12                         if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
13                         android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
14                     }
15                 }
16                 waitForIdle();
17 
18                 // second step
19                 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
20                 loadAndBindAllApps();

5、workspace加载数据

loadAndBindWorkspace()方法主要就是执行loadWorkspace()和 bindWorkspace()方法。
下面分别对这两个方法进行分析。
 1 /** Returns whether this is an upgrade path */
 2         private boolean loadAndBindWorkspace() {
 3             mIsLoadingAndBindingWorkspace = true;
 4 
 5             // Load the workspace
 6             if (DEBUG_LOADERS) {
 7                 Log.d(TAG, "loadAndBindWorkspace mWorkspaceLoaded=" + mWorkspaceLoaded);
 8             }
 9 
10             boolean isUpgradePath = false;
11             if (!mWorkspaceLoaded) {
12                 isUpgradePath = loadWorkspace();
13                 synchronized (LoaderTask.this) {
14                     if (mStopped) {
15                         return isUpgradePath;
16                     }
17                     mWorkspaceLoaded = true;
18                 }
19             }
20 
21             // Bind the workspace
22             bindWorkspace(-1, isUpgradePath);
23             return isUpgradePath;
24         }

 workspace的数据加载总的来说也是按照元素属性来区分加载,分为App快捷方式、Widget、Folder元素。

这几个元素分别加载到不同的容器里面。其中sItemsIdMap保存所有元素的id和ItemInfo组成的映射。其他

元素分别加载到3个不同的容器里面,用于后面绑定数据用。

  1   /** Returns whether this is an upgradge path */
  2         private boolean loadWorkspace() {
  3             final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
  4 
  5             final Context context = mContext;
  6             final ContentResolver contentResolver = context.getContentResolver();
  7             final PackageManager manager = context.getPackageManager();
  8             final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
  9             final boolean isSafeMode = manager.isSafeMode();
 10 
 11             LauncherAppState app = LauncherAppState.getInstance();
 12             DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 13             int countX = (int) grid.numColumns;
 14             int countY = (int) grid.numRows;
 15 
 16             // Make sure the default workspace is loaded, if needed
 17             LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);
 18 
 19             // Check if we need to do any upgrade-path logic
 20             boolean loadedOldDb = LauncherAppState.getLauncherProvider().justLoadedOldDb();
 21 
 22             synchronized (sBgLock) {
 23                 clearSBgDataStructures();
 24 
 25                 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
 26                 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
 27                 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
 28                 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
 29 
 30                 // +1 for the hotseat (it can be larger than the workspace)
 31                 // Load workspace in reverse order to ensure that latest items are loaded first (and
 32                 // before any earlier duplicates)
 33                 final HashMap<Long, ItemInfo[][]> occupied = new HashMap<Long, ItemInfo[][]>();
 34 
 35                 try {
 36                     final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
 37                     final int intentIndex = c.getColumnIndexOrThrow
 38                             (LauncherSettings.Favorites.INTENT);
 39                     final int titleIndex = c.getColumnIndexOrThrow
 40                             (LauncherSettings.Favorites.TITLE);
 41                     final int iconTypeIndex = c.getColumnIndexOrThrow(
 42                             LauncherSettings.Favorites.ICON_TYPE);
 43                     final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
 44                     final int iconPackageIndex = c.getColumnIndexOrThrow(
 45                             LauncherSettings.Favorites.ICON_PACKAGE);
 46                     final int iconResourceIndex = c.getColumnIndexOrThrow(
 47                             LauncherSettings.Favorites.ICON_RESOURCE);
 48                     final int containerIndex = c.getColumnIndexOrThrow(
 49                             LauncherSettings.Favorites.CONTAINER);
 50                     final int itemTypeIndex = c.getColumnIndexOrThrow(
 51                             LauncherSettings.Favorites.ITEM_TYPE);
 52                     final int appWidgetIdIndex = c.getColumnIndexOrThrow(
 53                             LauncherSettings.Favorites.APPWIDGET_ID);
 54                     final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
 55                             LauncherSettings.Favorites.APPWIDGET_PROVIDER);
 56                     final int screenIndex = c.getColumnIndexOrThrow(
 57                             LauncherSettings.Favorites.SCREEN);
 58                     final int cellXIndex = c.getColumnIndexOrThrow
 59                             (LauncherSettings.Favorites.CELLX);
 60                     final int cellYIndex = c.getColumnIndexOrThrow
 61                             (LauncherSettings.Favorites.CELLY);
 62                     final int spanXIndex = c.getColumnIndexOrThrow
 63                             (LauncherSettings.Favorites.SPANX);
 64                     final int spanYIndex = c.getColumnIndexOrThrow(
 65                             LauncherSettings.Favorites.SPANY);
 66                     //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
 67                     //final int displayModeIndex = c.getColumnIndexOrThrow(
 68                     //        LauncherSettings.Favorites.DISPLAY_MODE);
 69 
 70                     ShortcutInfo info;
 71                     String intentDescription;
 72                     LauncherAppWidgetInfo appWidgetInfo;
 73                     int container;
 74                     long id;
 75                     Intent intent;
 76 
 77                     while (!mStopped && c.moveToNext()) {
 78                         AtomicBoolean deleteOnItemOverlap = new AtomicBoolean(false);
 79                         try {
 80                             int itemType = c.getInt(itemTypeIndex);
 81 
 82                             switch (itemType) {
 83                             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
 84                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
 85                                 id = c.getLong(idIndex);
 86                                 intentDescription = c.getString(intentIndex);
 87                                 try {
 88                                     intent = Intent.parseUri(intentDescription, 0);
 89                                     ComponentName cn = intent.getComponent();
 90                                     if (cn != null && !isValidPackageComponent(manager, cn)) {
 91                                         if (!mAppsCanBeOnRemoveableStorage) {
 92                                             // Log the invalid package, and remove it from the db
 93                                             Launcher.addDumpLog(TAG, "Invalid package removed: " + cn, true);
 94                                             itemsToRemove.add(id);
 95                                         } else {
 96                                             // If apps can be on external storage, then we just
 97                                             // leave them for the user to remove (maybe add
 98                                             // visual treatment to it)
 99                                             Launcher.addDumpLog(TAG, "Invalid package found: " + cn, true);
100                                         }
101                                         continue;
102                                     }
103                                 } catch (URISyntaxException e) {
104                                     Launcher.addDumpLog(TAG, "Invalid uri: " + intentDescription, true);
105                                     continue;
106                                 }
107 
108                                 if (itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
109                                     info = getShortcutInfo(manager, intent, context, c, iconIndex,
110                                             titleIndex, mLabelCache);
111                                 } else {
112                                     info = getShortcutInfo(c, context, iconTypeIndex,
113                                             iconPackageIndex, iconResourceIndex, iconIndex,
114                                             titleIndex);
115 
116                                     // App shortcuts that used to be automatically added to Launcher
117                                     // didn't always have the correct intent flags set, so do that
118                                     // here
119                                     if (intent.getAction() != null &&
120                                         intent.getCategories() != null &&
121                                         intent.getAction().equals(Intent.ACTION_MAIN) &&
122                                         intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
123                                         intent.addFlags(
124                                             Intent.FLAG_ACTIVITY_NEW_TASK |
125                                             Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
126                                     }
127                                 }
128 
129                                 if (info != null) {
130                                     info.id = id;
131                                     info.intent = intent;
132                                     container = c.getInt(containerIndex);
133                                     info.container = container;
134                                     info.screenId = c.getInt(screenIndex);
135                                     info.cellX = c.getInt(cellXIndex);
136                                     info.cellY = c.getInt(cellYIndex);
137                                     info.spanX = 1;
138                                     info.spanY = 1;
139                                     // Skip loading items that are out of bounds
140                                     if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
141                                         if (checkItemDimensions(info)) {
142                                             Launcher.addDumpLog(TAG, "Skipped loading out of bounds shortcut: "
143                                                     + info + ", " + grid.numColumns + "x" + grid.numRows, true);
144                                             continue;
145                                         }
146                                     }
147                                     // check & update map of what's occupied
148                                     deleteOnItemOverlap.set(false);
149                                     if (!checkItemPlacement(occupied, info, deleteOnItemOverlap)) {
150                                         if (deleteOnItemOverlap.get()) {
151                                             itemsToRemove.add(id);
152                                         }
153                                         break;
154                                     }
155 
156                                     switch (container) {
157                                     case LauncherSettings.Favorites.CONTAINER_DESKTOP:
158                                     case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
159                                         sBgWorkspaceItems.add(info);
160                                         break;
161                                     default:
162                                         // Item is in a user folder
163                                         FolderInfo folderInfo =
164                                                 findOrMakeFolder(sBgFolders, container);
165                                         folderInfo.add(info);
166                                         break;
167                                     }
168                                     sBgItemsIdMap.put(info.id, info);
169 
170                                     // now that we've loaded everthing re-save it with the
171                                     // icon in case it disappears somehow.
172                                     queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
173                                 } else {
174                                     throw new RuntimeException("Unexpected null ShortcutInfo");
175                                 }
176                                 break;
177 
178                             case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
179                                 id = c.getLong(idIndex);
180                                 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
181 
182                                 folderInfo.title = c.getString(titleIndex);
183                                 folderInfo.id = id;
184                                 container = c.getInt(containerIndex);
185                                 folderInfo.container = container;
186                                 folderInfo.screenId = c.getInt(screenIndex);
187                                 folderInfo.cellX = c.getInt(cellXIndex);
188                                 folderInfo.cellY = c.getInt(cellYIndex);
189                                 folderInfo.spanX = 1;
190                                 folderInfo.spanY = 1;
191 
192                                 // Skip loading items that are out of bounds
193                                 if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
194                                     if (checkItemDimensions(folderInfo)) {
195                                         Log.d(TAG, "Skipped loading out of bounds folder");
196                                         continue;
197                                     }
198                                 }
199                                 // check & update map of what's occupied
200                                 deleteOnItemOverlap.set(false);
201                                 if (!checkItemPlacement(occupied, folderInfo,
202                                         deleteOnItemOverlap)) {
203                                     if (deleteOnItemOverlap.get()) {
204                                         itemsToRemove.add(id);
205                                     }
206                                     break;
207                                 }
208 
209                                 switch (container) {
210                                     case LauncherSettings.Favorites.CONTAINER_DESKTOP:
211                                     case LauncherSettings.Favorites.CONTAINER_HOTSEAT:
212                                         sBgWorkspaceItems.add(folderInfo);
213                                         break;
214                                 }
215 
216                                 sBgItemsIdMap.put(folderInfo.id, folderInfo);
217                                 sBgFolders.put(folderInfo.id, folderInfo);
218                                 break;
219 
220                             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
221                                 // Read all Launcher-specific widget details
222                                 int appWidgetId = c.getInt(appWidgetIdIndex);
223                                 String savedProvider = c.getString(appWidgetProviderIndex);
224 
225                                 id = c.getLong(idIndex);
226 
227                                 final AppWidgetProviderInfo provider =
228                                         widgets.getAppWidgetInfo(appWidgetId);
229 
230                                 if (!isSafeMode && (provider == null || provider.provider == null ||
231                                         provider.provider.getPackageName() == null)) {
232                                     String log = "Deleting widget that isn't installed anymore: id="
233                                         + id + " appWidgetId=" + appWidgetId;
234                                     Log.e(TAG, log);
235                                     Launcher.addDumpLog(TAG, log, false);
236                                     itemsToRemove.add(id);
237                                 } else {
238                                     appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
239                                             provider.provider);
240                                     appWidgetInfo.id = id;
241                                     appWidgetInfo.screenId = c.getInt(screenIndex);
242                                     appWidgetInfo.cellX = c.getInt(cellXIndex);
243                                     appWidgetInfo.cellY = c.getInt(cellYIndex);
244                                     appWidgetInfo.spanX = c.getInt(spanXIndex);
245                                     appWidgetInfo.spanY = c.getInt(spanYIndex);
246                                     int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
247                                     appWidgetInfo.minSpanX = minSpan[0];
248                                     appWidgetInfo.minSpanY = minSpan[1];
249 
250                                     container = c.getInt(containerIndex);
251                                     if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
252                                         container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
253                                         Log.e(TAG, "Widget found where container != " +
254                                             "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
255                                         continue;
256                                     }
257 
258                                     appWidgetInfo.container = c.getInt(containerIndex);
259                                     // Skip loading items that are out of bounds
260                                     if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
261                                         if (checkItemDimensions(appWidgetInfo)) {
262                                             Log.d(TAG, "Skipped loading out of bounds app widget");
263                                             continue;
264                                         }
265                                     }
266                                     // check & update map of what's occupied
267                                     deleteOnItemOverlap.set(false);
268                                     if (!checkItemPlacement(occupied, appWidgetInfo,
269                                             deleteOnItemOverlap)) {
270                                         if (deleteOnItemOverlap.get()) {
271                                             itemsToRemove.add(id);
272                                         }
273                                         break;
274                                     }
275                                     String providerName = provider.provider.flattenToString();
276                                     if (!providerName.equals(savedProvider)) {
277                                         ContentValues values = new ContentValues();
278                                         values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
279                                                 providerName);
280                                         String where = BaseColumns._ID + "= ?";
281                                         String[] args = {Integer.toString(c.getInt(idIndex))};
282                                         contentResolver.update(contentUri, values, where, args);
283                                     }
284                                     sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
285                                     sBgAppWidgets.add(appWidgetInfo);
286                                 }
287                                 break;
288                             }
289                         } catch (Exception e) {
290                             Launcher.addDumpLog(TAG, "Desktop items loading interrupted: " + e, true);
291                         }
292                     }
293                 } finally {
294                     if (c != null) {
295                         c.close();
296                     }
297                 }
298 
299                 // Break early if we've stopped loading
300                 if (mStopped) {
301                     clearSBgDataStructures();
302                     return false;
303                 }
304 
305                 if (itemsToRemove.size() > 0) {
306                     ContentProviderClient client = contentResolver.acquireContentProviderClient(
307                             LauncherSettings.Favorites.CONTENT_URI);
308                     // Remove dead items
309                     for (long id : itemsToRemove) {
310                         if (DEBUG_LOADERS) {
311                             Log.d(TAG, "Removed id = " + id);
312                         }
313                         // Don't notify content observers
314                         try {
315                             client.delete(LauncherSettings.Favorites.getContentUri(id, false),
316                                     null, null);
317                         } catch (RemoteException e) {
318                             Log.w(TAG, "Could not remove id = " + id);
319                         }
320                     }
321                 }
322 
323                 if (loadedOldDb) {
324                     long maxScreenId = 0;
325                     // If we're importing we use the old screen order.
326                     for (ItemInfo item: sBgItemsIdMap.values()) {
327                         long screenId = item.screenId;
328                         if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
329                                 !sBgWorkspaceScreens.contains(screenId)) {
330                             sBgWorkspaceScreens.add(screenId);
331                             if (screenId > maxScreenId) {
332                                 maxScreenId = screenId;
333                             }
334                         }
335                     }
336                     Collections.sort(sBgWorkspaceScreens);
337 
338                     LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
339                     updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
340 
341                     // Update the max item id after we load an old db
342                     long maxItemId = 0;
343                     // If we're importing we use the old screen order.
344                     for (ItemInfo item: sBgItemsIdMap.values()) {
345                         maxItemId = Math.max(maxItemId, item.id);
346                     }
347                     LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
348                 } else {
349                     TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
350                     for (Integer i : orderedScreens.keySet()) {
351                         sBgWorkspaceScreens.add(orderedScreens.get(i));
352                     }
353 
354                     // Remove any empty screens
355                     ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
356                     for (ItemInfo item: sBgItemsIdMap.values()) {
357                         long screenId = item.screenId;
358                         if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
359                                 unusedScreens.contains(screenId)) {
360                             unusedScreens.remove(screenId);
361                         }
362                     }
363 
364                     // If there are any empty screens remove them, and update.
365                     if (unusedScreens.size() != 0) {
366                         sBgWorkspaceScreens.removeAll(unusedScreens);
367                         updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
368                     }
369                 }
370 
371                 if (DEBUG_LOADERS) {
372                     Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
373                     Log.d(TAG, "workspace layout: ");
374                     int nScreens = occupied.size();
375                     for (int y = 0; y < countY; y++) {
376                         String line = "";
377 
378                         Iterator<Long> iter = occupied.keySet().iterator();
379                         while (iter.hasNext()) {
380                             long screenId = iter.next();
381                             if (screenId > 0) {
382                                 line += " | ";
383                             }
384                             for (int x = 0; x < countX; x++) {
385                                 line += ((occupied.get(screenId)[x][y] != null) ? "#" : ".");
386                             }
387                         }
388                         Log.d(TAG, "[ " + line + " ]");
389                     }
390                 }
391             }
392             return loadedOldDb;
393         }
View Code

6、workspace绑定数据

Launcher的内容绑定分为五步:分别对应着startBinding()、bindItems()、bindFolders()、 bindAppWidgets()、

finishBindingItems()的调用。下面针对bindWorkspace做个简单的流程分析。

  1    /**
  2          * Binds all loaded data to actual views on the main thread.
  3          */
  4         private void bindWorkspace(int synchronizeBindPage, final boolean isUpgradePath) {
  5             final long t = SystemClock.uptimeMillis();
  6             Runnable r;
  7 
  8             // Don't use these two variables in any of the callback runnables.
  9             // Otherwise we hold a reference to them.
 10             final Callbacks oldCallbacks = mCallbacks.get();
 11             if (oldCallbacks == null) {
 12                 // This launcher has exited and nobody bothered to tell us.  Just bail.
 13                 Log.w(TAG, "LoaderTask running with no launcher");
 14                 return;
 15             }
 16 
 17             final boolean isLoadingSynchronously = (synchronizeBindPage > -1);
 18             final int currentScreen = isLoadingSynchronously ? synchronizeBindPage :
 19                 oldCallbacks.getCurrentWorkspaceScreen();
 20 
 21             // Load all the items that are on the current page first (and in the process, unbind
 22             // all the existing workspace items before we call startBinding() below.
 23             unbindWorkspaceItemsOnMainThread();
 24             ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
 25             ArrayList<LauncherAppWidgetInfo> appWidgets =
 26                     new ArrayList<LauncherAppWidgetInfo>();
 27             HashMap<Long, FolderInfo> folders = new HashMap<Long, FolderInfo>();
 28             HashMap<Long, ItemInfo> itemsIdMap = new HashMap<Long, ItemInfo>();
 29             ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
 30             synchronized (sBgLock) {
 31                 workspaceItems.addAll(sBgWorkspaceItems);
 32                 appWidgets.addAll(sBgAppWidgets);
 33                 folders.putAll(sBgFolders);
 34                 itemsIdMap.putAll(sBgItemsIdMap);
 35                 orderedScreenIds.addAll(sBgWorkspaceScreens);
 36             }
 37 
 38             ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
 39             ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
 40             ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
 41                     new ArrayList<LauncherAppWidgetInfo>();
 42             ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
 43                     new ArrayList<LauncherAppWidgetInfo>();
 44             HashMap<Long, FolderInfo> currentFolders = new HashMap<Long, FolderInfo>();
 45             HashMap<Long, FolderInfo> otherFolders = new HashMap<Long, FolderInfo>();
 46 
 47             // Separate the items that are on the current screen, and all the other remaining items
 48             filterCurrentWorkspaceItems(currentScreen, workspaceItems, currentWorkspaceItems,
 49                     otherWorkspaceItems);
 50             filterCurrentAppWidgets(currentScreen, appWidgets, currentAppWidgets,
 51                     otherAppWidgets);
 52             filterCurrentFolders(currentScreen, itemsIdMap, folders, currentFolders,
 53                     otherFolders);
 54             sortWorkspaceItemsSpatially(currentWorkspaceItems);
 55             sortWorkspaceItemsSpatially(otherWorkspaceItems);
 56 
 57             // Tell the workspace that we're about to start binding items
 58             r = new Runnable() {
 59                 public void run() {
 60                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
 61                     if (callbacks != null) {
 62                         callbacks.startBinding();
 63                     }
 64                 }
 65             };
 66             runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
 67 
 68             bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
 69 
 70             // Load items on the current page
 71             bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
 72                     currentFolders, null);
 73             if (isLoadingSynchronously) {
 74                 r = new Runnable() {
 75                     public void run() {
 76                         Callbacks callbacks = tryGetCallbacks(oldCallbacks);
 77                         if (callbacks != null) {
 78                             callbacks.onPageBoundSynchronously(currentScreen);
 79                         }
 80                     }
 81                 };
 82                 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
 83             }
 84 
 85             // Load all the remaining pages (if we are loading synchronously, we want to defer this
 86             // work until after the first render)
 87             mDeferredBindRunnables.clear();
 88             bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
 89                     (isLoadingSynchronously ? mDeferredBindRunnables : null));
 90 
 91             // Tell the workspace that we're done binding items
 92             r = new Runnable() {
 93                 public void run() {
 94                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
 95                     if (callbacks != null) {
 96                         callbacks.finishBindingItems(isUpgradePath);
 97                     }
 98 
 99                     // If we're profiling, ensure this is the last thing in the queue.
100                     if (DEBUG_LOADERS) {
101                         Log.d(TAG, "bound workspace in "
102                             + (SystemClock.uptimeMillis()-t) + "ms");
103                     }
104 
105                     mIsLoadingAndBindingWorkspace = false;
106                 }
107             };
108             if (isLoadingSynchronously) {
109                 mDeferredBindRunnables.add(r);
110             } else {
111                 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
112             }
113         }

 上面就是Launcher的workspace绑定数据的过程,跟加载数据过程很相似,也是区分3中类型的元素进行加载。

下面我们总结一下,workspace的加载和绑定数据的过程。我们现在回头看,可以发现,其实workspace里面就是

存放了3中数据ItemInfo、FolderInfo、LauncherAppWidgetInfo。分别对应我们的APP快捷方式、文件夹、Widget

数据。其中FolderInfo、LauncherAppWidgetInfo都是继承了ItemInfo。数据加载过程,就是从Launcher的数据库

读取数据然后按元素属性分别放到3个ArrayList里面。绑定数据过程就是把3个ArrayList的队列关联到Launcher界面里面。

 7、ALL APP数据加载绑定

 1  private void loadAllApps() {
 2             final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
 3 
 4             final Callbacks oldCallbacks = mCallbacks.get();
 5             if (oldCallbacks == null) {
 6                 // This launcher has exited and nobody bothered to tell us.  Just bail.
 7                 Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
 8                 return;
 9             }
10 
11             final PackageManager packageManager = mContext.getPackageManager();
12             final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
13             mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
14 
15             // Clear the list of apps
16             mBgAllAppsList.clear();
17 
18             // Query for the set of apps
19             final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
20             List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
21             if (DEBUG_LOADERS) {
22                 Log.d(TAG, "queryIntentActivities took "
23                         + (SystemClock.uptimeMillis()-qiaTime) + "ms");
24                 Log.d(TAG, "queryIntentActivities got " + apps.size() + " apps");
25             }
26             // Fail if we don't have any apps
27             if (apps == null || apps.isEmpty()) {
28                 return;
29             }
30             // Sort the applications by name
31             final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
32             Collections.sort(apps,
33                     new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));
34             if (DEBUG_LOADERS) {
35                 Log.d(TAG, "sort took "
36                         + (SystemClock.uptimeMillis()-sortTime) + "ms");
37             }
38 
39             // Create the ApplicationInfos
40             for (int i = 0; i < apps.size(); i++) {
41                 ResolveInfo app = apps.get(i);
42                 // This builds the icon bitmaps.
43                 mBgAllAppsList.add(new AppInfo(packageManager, app,
44                         mIconCache, mLabelCache));
45             }
46 
47             // Huh? Shouldn't this be inside the Runnable below?
48             final ArrayList<AppInfo> added = mBgAllAppsList.added;
49             mBgAllAppsList.added = new ArrayList<AppInfo>();
50 
51             // Post callback on main thread
52             mHandler.post(new Runnable() {
53                 public void run() {
54                     final long bindTime = SystemClock.uptimeMillis();
55                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
56                     if (callbacks != null) {
57                         callbacks.bindAllApplications(added);
58                         if (DEBUG_LOADERS) {
59                             Log.d(TAG, "bound " + added.size() + " apps in "
60                                 + (SystemClock.uptimeMillis() - bindTime) + "ms");
61                         }
62                     } else {
63                         Log.i(TAG, "not binding apps: no Launcher activity");
64                     }
65                 }
66             });
67 
68             if (DEBUG_LOADERS) {
69                 Log.d(TAG, "Icons processed in "
70                         + (SystemClock.uptimeMillis() - loadTime) + "ms");
71             }
72         }
 // Post callback on main thread  很重要

AppInfo由四部分组成

   List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
// Create the ApplicationInfos for (int i = 0; i < apps.size(); i++) { ResolveInfo app = apps.get(i); // This builds the icon bitmaps. mBgAllAppsList.add(new AppInfo(packageManager, app, mIconCache, mLabelCache)); }

AllAPP的数据加载和绑定跟workspace的差不多,也是先加载数据然后绑定数据,通知Launcher。加载数据的时候

从PackageManager获取所有已经安装的APK包信息,然后过滤只包含需要显示在所有应用列表的应用,需要包含

ACTION_MAIN和CATEGORY_LAUNCHER两个属性。这个我们在编写应用程序的时候都应该知道。

AllAPP加载跟workspace不同的地方是加载的同时,完成数据绑定的操作,也就是说第一次加载AllAPP页面的数据,

会同时绑定数据到Launcher。第二次需要加载的时候,只会把数据直接绑定到Launcher,而不会重新搜索加载数据。

Launcher启动加载和绑定数据就是这样完成。绑定完数据,Launcher就可以运行。

绑定数据,回调接口

 1    /**
 2      * Add the icons for all apps.
 3      *
 4      * Implementation of the method from LauncherModel.Callbacks.
 5      */
 6     public void bindAllApplications(final ArrayList<AppInfo> apps) {
 7         if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
 8             if (mIntentsOnWorkspaceFromUpgradePath != null) {
 9                 if (LauncherModel.UPGRADE_USE_MORE_APPS_FOLDER) {
10                     getHotseat().addAllAppsFolder(mIconCache, apps,
11                             mIntentsOnWorkspaceFromUpgradePath, Launcher.this, mWorkspace);
12                 }
13                 mIntentsOnWorkspaceFromUpgradePath = null;
14             }
15         } else {
16             if (mAppsCustomizeContent != null) {
17                 mAppsCustomizeContent.setApps(apps);
18             }
19         }
20     }

8、ALL APP显示:

  public void syncAppsPageItems(int page, boolean immediate) {
        // ensure that we have the right number of items on the pages
        final boolean isRtl = isLayoutRtl();
        int numCells = mCellCountX * mCellCountY;
        int startIndex = page * numCells;
        int endIndex = Math.min(startIndex + numCells, mApps.size());
        AppsCustomizeCellLayout layout = (AppsCustomizeCellLayout) getPageAt(page);

        layout.removeAllViewsOnPage();
        ArrayList<Object> items = new ArrayList<Object>();
        ArrayList<Bitmap> images = new ArrayList<Bitmap>();
        for (int i = startIndex; i < endIndex; ++i) {
            AppInfo info = mApps.get(i);
            PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(
                    R.layout.apps_customize_application, layout, false);
            icon.applyFromApplicationInfo(info, true, this);
            icon.setOnClickListener(this);
            icon.setOnLongClickListener(this);
            icon.setOnTouchListener(this);
            icon.setOnKeyListener(this);

            int index = i - startIndex;
            int x = index % mCellCountX;
            int y = index / mCellCountX;
            if (isRtl) {
                x = mCellCountX - x - 1;
            }
            layout.addViewToCellLayout(icon, -1, i, new CellLayout.LayoutParams(x,y, 1,1), false);

            items.add(info);
            images.add(info.iconBitmap);
        }

        enableHwLayersOnVisiblePages();
    }
1  public void applyFromApplicationInfo(AppInfo info, boolean scaleUp,
2             PagedViewIcon.PressedCallback cb) {
3         mIcon = info.iconBitmap;
4         mPressedCallback = cb;
5         setCompoundDrawables(null, Utilities.createIconDrawable(mIcon),
6                 null, null);
7         setText(info.title);
8         setTag(info);
9     }

将图标和title加载到控件上

启动应用程序:

 1  @Override
 2     public void onClick(View v) {
 3         // When we have exited all apps or are in transition, disregard clicks
 4         if (!mLauncher.isAllAppsVisible() ||
 5                 mLauncher.getWorkspace().isSwitchingState()) return;
 6 
 7         if (v instanceof PagedViewIcon) {
 8             // Animate some feedback to the click
 9             final AppInfo appInfo = (AppInfo) v.getTag();
10 
11             // Lock the drawable state to pressed until we return to Launcher
12             if (mPressedIcon != null) {
13                 mPressedIcon.lockDrawableState();
14             }
15             mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
16             mLauncher.getStats().recordLaunch(appInfo.intent);
17         } else if (v instanceof PagedViewWidget) {
18             // Let the user know that they have to long press to add a widget
19             if (mWidgetInstructionToast != null) {
20                 mWidgetInstructionToast.cancel();
21             }
22             mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
23                 Toast.LENGTH_SHORT);
24             mWidgetInstructionToast.show();
25 
26             // Create a little animation to show that the widget can move
27             float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
28             final ImageView p = (ImageView) v.findViewById(R.id.widget_preview);
29             AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet();
30             ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY);
31             tyuAnim.setDuration(125);
32             ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f);
33             tydAnim.setDuration(100);
34             bounce.play(tyuAnim).before(tydAnim);
35             bounce.setInterpolator(new AccelerateInterpolator());
36             bounce.start();
37         }
38     }

卸载程序后,如何更新页面,探究:

 1     protected void invalidatePageData(int currentPage, boolean immediateAndOnly) {
 2         if (!mIsDataReady) {
 3             return;
 4         }
 5 
 6         if (mContentIsRefreshable) {
 7             // Force all scrolling-related behavior to end
 8             mScroller.forceFinished(true);
 9             mNextPage = INVALID_PAGE;
10 
11             // Update all the pages
12             syncPages();
13 
14             // We must force a measure after we've loaded the pages to update the content width and
15             // to determine the full scroll width
16             measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
17                     MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
18 
19             // Set a new page as the current page if necessary
20             if (currentPage > -1) {
21                 setCurrentPage(Math.min(getPageCount() - 1, currentPage));
22             }
23 
24             // Mark each of the pages as dirty
25             final int count = getChildCount();
26             mDirtyPageContent.clear();
27             for (int i = 0; i < count; ++i) {
28                 mDirtyPageContent.add(true);
29             }
30 
31             // Load any pages that are necessary for the current window of views
32             loadAssociatedPages(mCurrentPage, immediateAndOnly);
33             requestLayout();
34         }
35         if (isPageMoving()) {
36             // If the page is moving, then snap it to the final position to ensure we don't get
37             // stuck between pages
38             snapToDestination();
39         }
40     }
查询loadAssociatedPages

 安装程序后桌面出错:

原文地址:https://www.cnblogs.com/hixin/p/4382656.html