《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);
    }

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     }

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         }

 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         }

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         }

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就可以运行。

时间: 2024-10-26 08:22:32

《Android进阶》之第二篇 launcher的相关文章

Android开源项目第二篇——工具库篇

本文为那些不错的Android开源项目第二篇——开发工具库篇,主要介绍常用的开发库,包括依赖注入框架.图片缓存.网络相关.数据库ORM建模.Android公共库.Android 高版本向低版本兼容.多媒体相关及其他. Android开源项目系列汇总已完成,包括: Android开源项目第一篇——个性化控件(View)篇 Android开源项目第二篇——工具库篇 Android开源项目第三篇——优秀项目篇 Android开源项目第四篇——开发及测试工具篇 Android开源项目第五篇——优秀个人和

Android JNI入门第二篇——Java参数类型与本地参数类型对照

前面一篇通过简单的例子介绍了android中JNI的使用.这一篇从基础上了解一些Java参数类型与本地参数类型区别. 1)        java中的返回值void和JNI中的void是完全对应的哦!(仅仅一个而已). 2)        java中的基本数据类型(byte ,short ,int,long,float,double ,boolean,char-8种)在JNI中对应的数据类型只要在前面加上j就对应了(jbyte ,jshort ,jint,jlong,jfloat,jdouble

Android基础学习第二篇—Activity

写在前面的话: 1. 最近在自学Android,也是边看书边写一些Demo,由于知识点越来越多,脑子越来越记不清楚,所以打算写成读书笔记,供以后查看,也算是把自己学到所理解的东西写出来,献丑,如有不对的地方,希望大家给与指正. 2. 由于类似于读书笔记,可能格式神马的会比较随(hen)意(chou),大家看着受不了,可以使劲吐槽. *************************************我只是分割线***************************************

Android学习笔记(第二篇)View中的五大布局

PS:人不要低估自己的实力,但是也不能高估自己的能力.凡事谦为本... 学习内容: 1.用户界面View中的五大布局... i.首先介绍一下view的概念   view是什么呢?我们已经知道一个Activity是Android的显示层,但是Activity是不能直接显示在屏幕上的,它也像JSP那样,显示的东西是html,那么Android也不例外,Activity是一个抽象的壳子,而显示的东西就是view或者是viewgroup(图形用户组件)....   有了这个概念,我们就清楚view是如何

深入理解javascript函数进阶系列第二篇——函数柯里化

前面的话 函数柯里化currying的概念最早由俄国数学家Moses Schönfinkel发明,而后由著名的数理逻辑学家Haskell Curry将其丰富和发展,currying由此得名.本文将详细介绍函数柯里化(curring) 定义 currying又称部分求值.一个currying的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来.待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值 从

Python进阶【第二篇】多线程、消息队列queue

1.Python多线程.多进程 目的提高并发 1.一个应用程序,可以有多进程和多线程 2.默认:单进程,单线程 3.单进程,多线程 IO操作,不占用CPU python的多线程:IO操作,多线程提供并发 计算性操作 多进程提高并发 4.GIL,全局解释器锁 总结: 多进程,多线程,提供并发 IO密集型:多线程 计算密集型:多进程 2.threading模块 threading模块对象 描述 Thread 表示一个线程的执行的对象 Lock 锁原语对象 RLock 可重入锁对象.使单线程可以再次获

Android开源项目第一篇——个性化控件(View)篇

本文为那些不错的Android开源项目第一篇——个性化控件(View)篇,主要介绍Android上那些不错个性化的View,包括ListView.ActionBar.Menu.ViewPager.Gallery.GridView.ImageView.ProgressBar及其他如Dialog.Toast.EditText.TableView.Activity Animation等等. Android开源项目系列汇总已完成,包括: Android开源项目第一篇——个性化控件(View)篇 Andr

Android Metro风格的Launcher开发系列第二篇

前言: 各位小伙伴们请原谅我隔了这么久才开始写这一系列的第二篇博客,没办法忙新产品发布,好了废话不说了,先回顾一下:在我的上一篇博客Android Metro风格的Launcher开发系列第一篇写了如何配置Android开发环境,只是用文字和图片展示了开发Metro风格Launcher的初步设计和产品要求,这一篇文章将会从代码上讲解如何实现对应的UI效果,好了,评书开讲! Launcher主体框架实现: Launcher主体框架我选用的是大家所熟悉的ViewPager控件,因为ViewPager

【转载】Android Metro风格的Launcher开发系列第二篇

前言: 各位小伙伴们请原谅我隔了这么久才开始写这一系列的第二篇博客,没办法忙新产品发布,好了废话不说了,先回顾一下:在我的上一篇博客Android Metro风格的Launcher开发系列第一篇写了如何配置Android开发环境,只是用文字和图片展示了开发Metro风格Launcher的初步设计和产品要求,这一篇文章将会从代码上讲解如何实现对应的UI效果,好了,评书开讲! Launcher主体框架实现: Launcher主体框架我选用的是大家所熟悉的ViewPager控件,因为ViewPager