前面两篇文章介绍了AMS的启动,已经AMS启动之后启动Launcher的第一个Activity的过程。讲完上面那些东西,现在就说一下Launcher界面的加载过程。
@Override protected void onCreate(Bundle savedInstanceState) { if (DEBUG_STRICT_MODE) { //调试摸下走这里 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() // or .detectAll() for all detectable problems .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); } if (LauncherAppState.PROFILE_STARTUP) { Trace.beginSection("Launcher-onCreate"); } if (mLauncherCallbacks != null) { mLauncherCallbacks.preOnCreate(); } WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this); wallpaperColorInfo.setOnThemeChangeListener(this); overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText()); super.onCreate(savedInstanceState); //步骤1-获取配置Launcher启动信息对象实例 LauncherAppState app = LauncherAppState.getInstance(this); // Load configuration-specific DeviceProfile //获取加载设备配置信息 mDeviceProfile = app.getInvariantDeviceProfile().getDeviceProfile(this); if (isInMultiWindowModeCompat()) { Display display = getWindowManager().getDefaultDisplay(); Point mwSize = new Point(); display.getSize(mwSize); mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize); } mOrientation = getResources().getConfiguration().orientation; mSharedPrefs = Utilities.getPrefs(this); mIsSafeModeEnabled = getPackageManager().isSafeMode(); //获取加载界面数据的对象实例 mModel = app.setLauncher(this); mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout()); mIconCache = app.getIconCache(); mAccessibilityDelegate = new LauncherAccessibilityDelegate(this); //拖拽控制器 mDragController = new DragController(this); mAllAppsController = new AllAppsTransitionController(this); mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController); mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); mAppWidgetHost = new LauncherAppWidgetHost(this); if (Utilities.ATLEAST_MARSHMALLOW) { mAppWidgetHost.addProviderChangeListener(this); } mAppWidgetHost.startListening(); // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here, // this also ensures that any synchronous binding below doesn't re-trigger another // LauncherModel load. mPaused = false; //获取主界面View mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null); //获取各个控件的View setupViews(); mDeviceProfile.layout(this, false /* notifyListeners */); loadExtractedColorsAndColorItems(); mPopupDataProvider = new PopupDataProvider(this); ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE)) .addAccessibilityStateChangeListener(this); lockAllApps(); restoreState(savedInstanceState); if (LauncherAppState.PROFILE_STARTUP) { Trace.endSection(); } // We only load the page synchronously if the user rotates (or triggers a // configuration change) while launcher is in the foreground int currentScreen = PagedView.INVALID_RESTORE_PAGE; if (savedInstanceState != null) { currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen); } //步骤三-加载各个界面,加载所有的APP if (!mModel.startLoader(currentScreen)) { // If we are not binding synchronously, show a fade in animation when // the first page bind completes. mDragLayer.setAlpha(0); } else { // Pages bound synchronously. mWorkspace.setCurrentPage(currentScreen); setWorkspaceLoading(true); } // For handling default keys mDefaultKeySsb = new SpannableStringBuilder(); Selection.setSelection(mDefaultKeySsb, 0); mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation); // In case we are on a device with locked rotation, we should look at preferences to check // if the user has specifically allowed rotation. if (!mRotationEnabled) { mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext()); mRotationPrefChangeHandler = new RotationPrefChangeHandler(); mSharedPrefs.registerOnSharedPreferenceChangeListener(mRotationPrefChangeHandler); } if (PinItemDragListener.handleDragRequest(this, getIntent())) { // Temporarily enable the rotation mRotationEnabled = true; } // On large interfaces, or on devices that a user has specifically enabled screen rotation, // we want the screen to auto-rotate based on the current orientation setOrientation(); setContentView(mLauncherView); // Listen for broadcasts IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone registerReceiver(mReceiver, filter); mShouldFadeInScrim = true; getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW, Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)); if (mLauncherCallbacks != null) { mLauncherCallbacks.onCreate(savedInstanceState); } }
这部分代码差不多要完成Launcher显示所需要的数据的加载工作,我这里主要分三个步骤。
下面就来讲解步骤一和步骤三
public static LauncherAppState getInstance(final Context context) { if (INSTANCE == null) { if (Looper.myLooper() == Looper.getMainLooper()) {//是在主线程,进行实例化 INSTANCE = new LauncherAppState(context.getApplicationContext()); } else { try { return new MainThreadExecutor().submit(new Callable这这部分过程中获取设备配置信息,计算图标,控件等大小,最后还要添加APP变化监听器。下面看一下获取设备配置信息() { @Override public LauncherAppState call() throws Exception { return LauncherAppState.getInstance(context); } }).get(); } catch (InterruptedException|ExecutionException e) { throw new RuntimeException(e); } } } return INSTANCE; } private LauncherAppState(Context context) { if (getLocalProvider(context) == null) { throw new RuntimeException( "Initializing LauncherAppState in the absence of LauncherProvider"); } Log.v(Launcher.TAG, "LauncherAppState initiated"); Preconditions.assertUIThread(); mContext = context; if (TestingUtils.MEMORY_DUMP_ENABLED) { TestingUtils.startTrackingMemory(mContext); } //设备基本配置信息 mInvariantDeviceProfile = new InvariantDeviceProfile(mContext); //图标大小 mIconCache = new IconCache(mContext, mInvariantDeviceProfile); mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache); //加载APP等操作对象实例化 mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext)); //添加监听APK变化的监听器 LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel); // Register intent receivers IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); // For handling managed profiles filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED); // For extracting colors from the wallpaper if (Utilities.ATLEAST_NOUGAT) { // TODO: add a broadcast entry to the manifest for pre-N. filter.addAction(Intent.ACTION_WALLPAPER_CHANGED); } mContext.registerReceiver(mModel, filter); UserManagerCompat.getInstance(mContext).enableAndResetCache(); new ConfigMonitor(mContext).register(); ExtractionUtils.startColorExtractionServiceIfNecessary(mContext); if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) { mNotificationBadgingObserver = null; } else { // Register an observer to rebind the notification listener when badging is re-enabled. mNotificationBadgingObserver = new SettingsObserver.Secure( mContext.getContentResolver()) { @Override public void onSettingChanged(boolean isNotificationBadgingEnabled) { if (isNotificationBadgingEnabled) { NotificationListener.requestRebind(new ComponentName( mContext, NotificationListener.class)); } } }; mNotificationBadgingObserver.register(NOTIFICATION_BADGING); } }
InvariantDeviceProfile(Context context) { WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); DisplayMetrics dm = new DisplayMetrics(); display.getMetrics(dm); Point smallestSize = new Point(); Point largestSize = new Point(); //获取显示的最大值与最小值 display.getCurrentSizeRange(smallestSize, largestSize); // This guarantees that width < height minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm); minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm); //从配置文件中找到最接近当前屏幕显示配置数据 ArrayList这段代码要完成根据获取到当前屏幕的显示数据与系统的配置文件数据找到最符合当前屏幕的配置文件,然后计算出图标大小之类的数据。closestProfiles = findClosestDeviceProfiles( minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context)); InvariantDeviceProfile interpolatedDeviceProfileOut = invDistWeightedInterpolate(minWidthDps, minHeightDps, closestProfiles); InvariantDeviceProfile closestProfile = closestProfiles.get(0); numRows = closestProfile.numRows; numColumns = closestProfile.numColumns; numHotseatIcons = closestProfile.numHotseatIcons; defaultLayoutId = closestProfile.defaultLayoutId; demoModeLayoutId = closestProfile.demoModeLayoutId; numFolderRows = closestProfile.numFolderRows; numFolderColumns = closestProfile.numFolderColumns; minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns; iconSize = interpolatedDeviceProfileOut.iconSize; landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize; iconBitmapSize = Utilities.pxFromDp(iconSize, dm); iconTextSize = interpolatedDeviceProfileOut.iconTextSize; fillResIconDpi = getLauncherIconDensity(iconBitmapSize); // If the partner customization apk contains any grid overrides, apply them // Supported overrides: numRows, numColumns, iconSize applyPartnerDeviceProfileOverrides(context, dm); Point realSize = new Point(); display.getRealSize(realSize); // The real size never changes. smallSide and largeSide will remain the // same in any orientation. int smallSide = Math.min(realSize.x, realSize.y); int largeSide = Math.max(realSize.x, realSize.y); landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize, largeSide, smallSide, true /* isLandscape */); portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize, smallSide, largeSide, false /* isLandscape */); // We need to ensure that there is enough extra space in the wallpaper // for the intended parallax effects if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) { defaultWallpaperSize = new Point( (int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)), largeSide); } else { defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide); } }
在确定了各种页面尺寸,图标大小等配置信息,会进行找到相应显示区域的View,关于显示区域的说明我还真是忘记了。这里我不在说明找到各个区域View这部分,直接进行到加载APP数据信息的部分
public boolean startLoader(int synchronousBindPage) { // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING); //停止所有没用的绑定工作线程 synchronized (mLock) { // Don't bother to start the thread if we know it's not going to do anything if (mCallbacks != null && mCallbacks.get() != null) { final Callbacks oldCallbacks = mCallbacks.get(); // Clear any pending bind-runnables from the synchronized load process. mUiExecutor.execute(new Runnable() { public void run() { oldCallbacks.clearPendingBinds(); } }); // If there is already one running, tell it to stop. //如果已经有一个任务开始启动了,这里就是停止此任务 stopLoader(); //用于接受加载任务的结果 LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel, mBgAllAppsList, synchronousBindPage, mCallbacks); if (mModelLoaded && !mIsLoaderTaskRunning) { // Divide the set of loaded items into those that we are binding synchronously, // and everything else that is to be bound normally (asynchronously). loaderResults.bindWorkspace(); // For now, continue posting the binding of AllApps as there are other // issues that arise from that. loaderResults.bindAllApps(); loaderResults.bindDeepShortcuts(); loaderResults.bindWidgets(); return true; } else {//加载走这里 startLoaderForResults(loaderResults); } } } return false; } public void startLoaderForResults(LoaderResults results) { synchronized (mLock) { stopLoader(); //创建任务,然后执行 mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results); runOnWorkerThread(mLoaderTask); } }
这部分代码还是比较容易理解的就是启动一个LoaderTask任务,加载数据就靠LoaderTask任务来进行。
public void run() { synchronized (this) { // Skip fast if we are already stopped. if (mStopped) { return; } } try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { long now = 0; if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace"); loadWorkspace();//加载工作区域 verifyNotStopped(); if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace"); mResults.bindWorkspace();//绑定工作区域 // Take a break if (DEBUG_LOADERS) { Log.d(TAG, "step 1 completed, wait for idle"); now = SystemClock.uptimeMillis(); } waitForIdle(); if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms"); verifyNotStopped();//等待工作区域的所有工作完成 // second step if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps"); loadAllApps();//加载所有的APP if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps"); verifyNotStopped(); mResults.bindAllApps();//绑定所有的APP数据到工作区域 verifyNotStopped(); if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache"); updateIconCache();//更新图标缓存 // Take a break if (DEBUG_LOADERS) { Log.d(TAG, "step 2 completed, wait for idle"); now = SystemClock.uptimeMillis(); } waitForIdle();//等待加载APP工作完成 if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms"); verifyNotStopped(); // third step if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts"); loadDeepShortcuts();//加载快捷图标 verifyNotStopped(); if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts"); mResults.bindDeepShortcuts();//绑定快捷图标 // Take a break if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle"); waitForIdle(); verifyNotStopped();//等待加载快捷图标结束 // fourth step if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets"); mBgDataModel.widgetsModel.update(mApp, null);//加载控件 verifyNotStopped(); if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets"); mResults.bindWidgets();//绑定控件到桌面 transaction.commit(); } catch (CancellationException e) { // Loader stopped, ignore if (DEBUG_LOADERS) { Log.d(TAG, "Loader cancelled", e); } } }
在Android8.1开始加载数据信息这步骤做了一些修改,现在是一步步加载数据。
private void loadWorkspace() { if (LauncherAppState.PROFILE_STARTUP) { Trace.beginSection("Loading Workspace"); } ...... synchronized (mBgDataModel) { mBgDataModel.clear(); final HashMap, Integer> installingPkgs = mPackageInstaller.updateAndGetActiveSessionCache(); mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context)); Map , ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>(); final LoaderCursor c = new LoaderCursor(contentResolver.query( LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp); HashMap , AppWidgetProviderInfo> widgetProvidersMap = null; try { final int appWidgetIdIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.APPWIDGET_ID); final int appWidgetProviderIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.APPWIDGET_PROVIDER); final int spanXIndex = c.getColumnIndexOrThrow (LauncherSettings.Favorites.SPANX); final int spanYIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.SPANY); final int rankIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.RANK); final int optionsIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.OPTIONS); final LongSparseArray allUsers = c.allUsers; final LongSparseArray quietMode = new LongSparseArray<>(); final LongSparseArray unlockedUsers = new LongSparseArray<>(); for (UserHandle user : mUserManager.getUserProfiles()) {//遍历每一个手机系统账户 long serialNo = mUserManager.getSerialNumberForUser(user); allUsers.put(serialNo, user); quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user)); boolean userUnlocked = mUserManager.isUserUnlocked(user); // We can only query for shortcuts when the user is unlocked. //当前用户没有锁定,则恢复桌面单独快捷方式 if (userUnlocked) { List pinnedShortcuts = mShortcutManager.queryForPinnedShortcuts(null, user); if (mShortcutManager.wasLastCallSuccess()) { for (ShortcutInfoCompat shortcut : pinnedShortcuts) { shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut); } } else { // Shortcut manager can fail due to some race condition when the // lock state changes too frequently. For the purpose of the loading // shortcuts, consider the user is still locked. userUnlocked = false; } } unlockedUsers.put(serialNo, userUnlocked); } ShortcutInfo info; LauncherAppWidgetInfo appWidgetInfo; Intent intent; String targetPkg; FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile()); while (!mStopped && c.moveToNext()) { try { if (c.user == null) {//用户信息为空,处理下一条数据 // User has been deleted, remove the item. c.markDeleted("User has been deleted"); continue; } boolean allowMissingTarget = false; switch (c.itemType) { //如果当前的数据信息是应用、快捷应用或者桌面快捷方式 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: intent = c.parseIntent(); if (intent == null) {//没有意图,只能进行下一条 c.markDeleted("Invalid or null intent"); continue; } int disabledState = quietMode.get(c.serialNumber) ? ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0; ComponentName cn = intent.getComponent(); //获取安装包的名称 targetPkg = cn == null ? intent.getPackage() : cn.getPackageName(); // if (!Process.myUserHandle().equals(c.user)) { if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { c.markDeleted("Legacy shortcuts are only allowed for default user"); continue; } else if (c.restoreFlag != 0) { // Don't restore items for other profiles. c.markDeleted("Restore from managed profile not supported"); continue; } } //包名为空并且此条数据类型不是快捷方式,进行下一条 if (TextUtils.isEmpty(targetPkg) && c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) { c.markDeleted("Only legacy shortcuts can have null package"); continue; } // If there is no target package, its an implicit intent // (legacy shortcut) which is always valid boolean validTarget = TextUtils.isEmpty(targetPkg) || mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user); if (cn != null && validTarget) {//apk存在并且快捷方式指向一个特定的控件 // If the apk is present and the shortcut points to a specific // component. // If the component is already present(此控件真实存在) if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) { // no special handling necessary for this item c.markRestored(); } else { if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) { // We allow auto install apps to have their intent // updated after an install. intent = pmHelper.getAppLaunchIntent(targetPkg, c.user); if (intent != null) { c.restoreFlag = 0; c.updater().put( LauncherSettings.Favorites.INTENT, intent.toUri(0)).commit(); cn = intent.getComponent(); } else { c.markDeleted("Unable to find a launch target"); continue; } } else { // The app is installed but the component is no // longer available. c.markDeleted("Invalid component removed: " + cn); continue; } } } // else if cn == null => can't infer much, leave it // else if !validPkg => could be restored icon or missing sd-card if (!TextUtils.isEmpty(targetPkg) && !validTarget) { // Points to a valid app (superset of cn != null) but the apk // is not available. if (c.restoreFlag != 0) { // Package is not yet available but might be // installed later. FileLog.d(TAG, "package not yet restored: " + targetPkg); if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) { // Restore has started once. } else if (installingPkgs.containsKey(targetPkg)) { // App restore has started. Update the flag c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED; c.updater().commit(); } else { c.markDeleted("Unrestored app removed: " + targetPkg); continue; } } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { // Package is present but not available. disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE; // Add the icon on the workspace anyway. allowMissingTarget = true; } else if (!isSdCardReady) { // SdCard is not ready yet. Package might get available, // once it is ready. Log.d(TAG, "Missing pkg, will check later: " + targetPkg); pendingPackages.addToList(c.user, targetPkg); // Add the icon on the workspace anyway. allowMissingTarget = true; } else { // Do not wait for external media load anymore. c.markDeleted("Invalid package removed: " + targetPkg); continue; } } if ((c.restoreFlag & ShortcutInfo.FLAG_SUPPORTS_WEB_UI) != 0) { validTarget = false; } if (validTarget) { // The shortcut points to a valid target (either no target // or something which is ready to be used) c.markRestored(); } boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() && !verifier.isItemInPreview(c.getInt(rankIndex)); if (c.restoreFlag != 0) {//如果是可以恢复的直接提取恢复信息 // Already verified above that user is same as default user info = c.getRestoredItemInfo(intent); } else if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {//不能恢复,但是是应用信息 info = c.getAppShortcutInfo( intent, allowMissingTarget, useLowResIcon); } else if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {//桌面快捷方式 ShortcutKey key = ShortcutKey.fromIntent(intent, c.user); if (unlockedUsers.get(c.serialNumber)) { ShortcutInfoCompat pinnedShortcut = shortcutKeyToPinnedShortcuts.get(key); if (pinnedShortcut == null) { // The shortcut is no longer valid. c.markDeleted("Pinned shortcut not found"); continue; } info = new ShortcutInfo(pinnedShortcut, context); final ShortcutInfo finalInfo = info; Provider fallbackIconProvider = new Provider () { @Override public Bitmap get() { // If the pinned deep shortcut is no longer published, // use the last saved icon instead of the default. return c.loadIcon(finalInfo); } }; info.iconBitmap = LauncherIcons .createShortcutIcon(pinnedShortcut, context, true /* badged */, fallbackIconProvider); if (pmHelper.isAppSuspended( pinnedShortcut.getPackage(), info.user)) { info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; } intent = info.intent; } else { // Create a shortcut info in disabled mode for now. info = c.loadSimpleShortcut(); info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER; } } else { // item type == ITEM_TYPE_SHORTCUT info = c.loadSimpleShortcut(); // Shortcuts are only available on the primary profile if (!TextUtils.isEmpty(targetPkg) && pmHelper.isAppSuspended(targetPkg, c.user)) { disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED; } // App shortcuts that used to be automatically added to Launcher // didn't always have the correct intent flags set, so do that // here if (intent.getAction() != null && intent.getCategories() != null && intent.getAction().equals(Intent.ACTION_MAIN) && intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); } } if (info != null) { c.applyCommonProperties(info); info.intent = intent; info.rank = c.getInt(rankIndex); info.spanX = 1; info.spanY = 1; info.isDisabled |= disabledState; if (isSafeMode && !Utilities.isSystemApp(context, intent)) { info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE; } if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { Integer progress = installingPkgs.get(targetPkg); if (progress != null) { info.setInstallProgress(progress); } else { info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE; } } c.checkAndAddItem(info, mBgDataModel); } else { throw new RuntimeException("Unexpected null ShortcutInfo"); } break; case LauncherSettings.Favorites.ITEM_TYPE_FOLDER://处理文件夹 FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id); c.applyCommonProperties(folderInfo); // Do not trim the folder label, as is was set by the user. folderInfo.title = c.getString(c.titleIndex); folderInfo.spanX = 1; folderInfo.spanY = 1; folderInfo.options = c.getInt(optionsIndex); // no special handling required for restored folders c.markRestored(); c.checkAndAddItem(folderInfo, mBgDataModel); break; case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET://处理APP的原生控件 if (FeatureFlags.GO_DISABLE_WIDGETS) { c.markDeleted("Only legacy shortcuts can have null package"); continue; } // Follow through case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET://处理自定义控件 // Read all Launcher-specific widget details boolean customWidget = c.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET; int appWidgetId = c.getInt(appWidgetIdIndex); String savedProvider = c.getString(appWidgetProviderIndex); final ComponentName component = ComponentName.unflattenFromString(savedProvider); final boolean isIdValid = !c.hasRestoreFlag( LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); final boolean wasProviderReady = !c.hasRestoreFlag( LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY); if (widgetProvidersMap == null) { widgetProvidersMap = mAppWidgetManager.getAllProvidersMap(); } final AppWidgetProviderInfo provider = widgetProvidersMap.get( new ComponentKey( ComponentName.unflattenFromString(savedProvider), c.user)); final boolean isProviderReady = isValidProvider(provider); if (!isSafeMode && !customWidget && wasProviderReady && !isProviderReady) { c.markDeleted( "Deleting widget that isn't installed anymore: " + provider); } else { if (isProviderReady) { appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, provider.provider); // The provider is available. So the widget is either // available or not available. We do not need to track // any future restore updates. int status = c.restoreFlag & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED & ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; if (!wasProviderReady) { // If provider was not previously ready, update the // status and UI flag. // Id would be valid only if the widget restore broadcast was received. if (isIdValid) { status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; } } appWidgetInfo.restoreStatus = status; } else { Log.v(TAG, "Widget restore pending id=" + c.id + " appWidgetId=" + appWidgetId + " status =" + c.restoreFlag); appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, component); appWidgetInfo.restoreStatus = c.restoreFlag; Integer installProgress = installingPkgs.get(component.getPackageName()); if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) { // Restore has started once. } else if (installProgress != null) { // App restore has started. Update the flag appWidgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; } else if (!isSafeMode) { c.markDeleted("Unrestored widget removed: " + component); continue; } appWidgetInfo.installProgress = installProgress == null ? 0 : installProgress; } if (appWidgetInfo.hasRestoreFlag( LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) { appWidgetInfo.bindOptions = c.parseIntent(); } c.applyCommonProperties(appWidgetInfo); appWidgetInfo.spanX = c.getInt(spanXIndex); appWidgetInfo.spanY = c.getInt(spanYIndex); appWidgetInfo.user = c.user; if (!c.isOnWorkspaceOrHotseat()) { c.markDeleted("Widget found where container != " + "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); continue; } if (!customWidget) { String providerName = appWidgetInfo.providerName.flattenToString(); if (!providerName.equals(savedProvider) || (appWidgetInfo.restoreStatus != c.restoreFlag)) { c.updater() .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName) .put(LauncherSettings.Favorites.RESTORED, appWidgetInfo.restoreStatus) .commit(); } } if (appWidgetInfo.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED) { String pkg = appWidgetInfo.providerName.getPackageName(); appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg); appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user; mIconCache.getTitleAndIconForApp( appWidgetInfo.pendingItemInfo, false); } c.checkAndAddItem(appWidgetInfo, mBgDataModel); } break; } } catch (Exception e) { Log.e(TAG, "Desktop items loading interrupted", e); } } } finally {//最后关闭游标防止内存泄漏 Utilities.closeSilently(c); } // Break early if we've stopped loading if (mStopped) { mBgDataModel.clear(); return; } ...... }
这部分代码主要是对数据库中APP信息,或者文件夹信息,或者是快捷图标信息进行分类处理。然后删除空文件夹、无用的快捷图标、无用的界面。在这个过程中有一个重要的操作,使用mBgDataModel进行数据清空,在处理的同时将整理好的数据添加到数据库中。在后来要进行界面item绑定时候就可以拿到处理好的数据,然后进行绑定。
public void bindWorkspace() { Runnable r; Callbacks callbacks = mCallbacks.get(); // Don't use these two variables in any of the callback runnables. // Otherwise we hold a reference to them. if (callbacks == null) { // This launcher has exited and nobody bothered to tell us. Just bail. Log.w(TAG, "LoaderTask running with no launcher"); return; } // Save a copy of all the bg-thread collections ArrayListworkspaceItems = new ArrayList<>(); ArrayList appWidgets = new ArrayList<>(); final ArrayList orderedScreenIds = new ArrayList<>(); synchronized (mBgDataModel) { workspaceItems.addAll(mBgDataModel.workspaceItems);//数据库中所有的workspaceItems信息 appWidgets.addAll(mBgDataModel.appWidgets);//数据库中处理好的控件信息 orderedScreenIds.addAll(mBgDataModel.workspaceScreens);//数据库中处理好的屏幕的信息 } final int currentScreen; {//无论如何获取到Launcher的第一个Screen int currScreen = mPageToBindFirst != PagedView.INVALID_RESTORE_PAGE ? mPageToBindFirst : callbacks.getCurrentWorkspaceScreen(); if (currScreen >= orderedScreenIds.size()) { // There may be no workspace screens (just hotseat items and an empty page). currScreen = PagedView.INVALID_RESTORE_PAGE; } currentScreen = currScreen; } final boolean validFirstPage = currentScreen >= 0; final long currentScreenId = validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID; // Separate the items that are on the current screen, and all the other remaining items ArrayList currentWorkspaceItems = new ArrayList<>(); ArrayList otherWorkspaceItems = new ArrayList<>(); ArrayList currentAppWidgets = new ArrayList<>(); ArrayList otherAppWidgets = new ArrayList<>(); //处理属于Items分类的的数据,分类为当前工作区,和不是当前工作的 filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems, otherWorkspaceItems); filterCurrentWorkspaceItems(currentScreenId, appWidgets, currentAppWidgets, otherAppWidgets); //进行排序 sortWorkspaceItemsSpatially(currentWorkspaceItems); sortWorkspaceItemsSpatially(otherWorkspaceItems); // Tell the workspace that we're about to start binding items //绑定items前的清理工作,告诉WorkSapce要开始绑定item了 r = new Runnable() { public void run() { Callbacks callbacks = mCallbacks.get(); if (callbacks != null) { callbacks.clearPendingBinds(); callbacks.startBinding(); } } }; mUiExecutor.execute(r); // 绑定工作区到分屏中去 mUiExecutor.execute(new Runnable() { @Override public void run() { Callbacks callbacks = mCallbacks.get(); if (callbacks != null) { callbacks.bindScreens(orderedScreenIds); } } }); Executor mainExecutor = mUiExecutor; // Load items on the current page. //加载当前屏幕也的items bindWorkspaceItems(currentWorkspaceItems, currentAppWidgets, mainExecutor); // In case of validFirstPage, only bind the first screen, and defer binding the // remaining screens after first onDraw (and an optional the fade animation whichever // happens later). // This ensures that the first screen is immediately visible (eg. during rotation) // In case of !validFirstPage, bind all pages one after other. final Executor deferredExecutor = validFirstPage ? new ViewOnDrawExecutor(mUiExecutor) : mainExecutor; mainExecutor.execute(new Runnable() { @Override public void run() { Callbacks callbacks = mCallbacks.get(); if (callbacks != null) { callbacks.finishFirstPageBind( validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null); } } }); //绑定其他屏幕items bindWorkspaceItems(otherWorkspaceItems, otherAppWidgets, deferredExecutor); // Tell the workspace that we're done binding items r = new Runnable() { public void run() { Callbacks callbacks = mCallbacks.get(); if (callbacks != null) { callbacks.finishBindingItems(); } } }; deferredExecutor.execute(r); //如果绑定工作还没有结束,通知页面绑定工作还在继续 if (validFirstPage) { r = new Runnable() { public void run() { Callbacks callbacks = mCallbacks.get(); if (callbacks != null) { // We are loading synchronously, which means, some of the pages will be // bound after first draw. Inform the callbacks that page binding is // not complete, and schedule the remaining pages. if (currentScreen != PagedView.INVALID_RESTORE_PAGE) { callbacks.onPageBoundSynchronously(currentScreen); } callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor); } } }; mUiExecutor.execute(r); } } /** Filters the set of items who are directly or indirectly (via another container) on the * specified screen. */ private <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId, ArrayList<T> allWorkspaceItems, ArrayList<T> currentScreenItems, ArrayList<T> otherScreenItems) { // Purge any null ItemInfos Iterator<T> iter = allWorkspaceItems.iterator(); while (iter.hasNext()) { ItemInfo i = iter.next(); if (i == null) { iter.remove(); } } // Order the set of items by their containers first, this allows use to walk through the // list sequentially, build up a list of containers that are in the specified screen, // as well as all items in those containers. Set itemsOnScreen = new HashSet<>(); Collections.sort(allWorkspaceItems, new Comparator () {//对Items进行排序 @Override public int compare(ItemInfo lhs, ItemInfo rhs) { return Utilities.longCompare(lhs.container, rhs.container); } }); for (T info : allWorkspaceItems) { if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {//桌面的APP的信息 if (info.screenId == currentScreenId) {//当前屏幕的item currentScreenItems.add(info); itemsOnScreen.add(info.id); } else { otherScreenItems.add(info); } } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {//属于界面下面四个固定item currentScreenItems.add(info); itemsOnScreen.add(info.id); } else { if (itemsOnScreen.contains(info.container)) {//属于当前屏幕文件夹的 currentScreenItems.add(info); itemsOnScreen.add(info.id); } else { otherScreenItems.add(info); } } } }
这段代码完成事情就比较多了。首先需要将不同屏幕的workspace进行加载绑定,然后就是对不同的workspace中item进行加载绑定。这部分主要是完成页面的绘制工作,也只有完成这些工作只有才能完整的在手机页面中显示APP。这里面还牵扯到屏幕绑定工作和item具体绑定流程没有说明,可自行跟踪查看源码。
private void loadAllApps() { final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; final Listprofiles = mUserManager.getUserProfiles(); // Clear the list of apps mBgAllAppsList.clear(); for (UserHandle user : profiles) {//账户信息 // Query for the set of apps final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; //获取所有的app的LauncherActivityInfo信息 final List apps = mLauncherApps.getActivityList(null, user); if (DEBUG_LOADERS) { Log.d(TAG, "getActivityList took " + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user); Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user); } // Fail if we don't have any apps // TODO: Fix this. Only fail for the current user. if (apps == null || apps.isEmpty()) { return; } boolean quietMode = mUserManager.isQuietModeEnabled(user); // Create the ApplicationInfos for (int i = 0; i < apps.size(); i++) { LauncherActivityInfo app = apps.get(i); // This builds the icon bitmaps. mBgAllAppsList.add(new AppInfo(app, user, quietMode), app); } ManagedProfileHeuristic.onAllAppsLoaded(mApp.getContext(), apps, user); } //在安装APP的时候会创建session数据,这里是获取 if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { // get all active sessions and add them to the all apps list for (PackageInstaller.SessionInfo info : mPackageInstaller.getAllVerifiedSessions()) { mBgAllAppsList.addPromiseApp(mApp.getContext(), PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info)); } } mBgAllAppsList.added = new ArrayList<>(); if (DEBUG_LOADERS) { Log.d(TAG, "All apps loaded in in " + (SystemClock.uptimeMillis() - loadTime) + "ms"); } }
这段代码就比较简洁了,就是通过Installer获取APP的包信息,已经session数据。
public void bindAllApps() { // shallow copy @SuppressWarnings("unchecked") final ArrayList这段代码就十多行,就是调用callback的回调函数进行app安装包信息的绑定。其实就是调用Launcher中的bindAllApplication完成绑定工作。list = (ArrayList ) mBgAllAppsList.data.clone(); Runnable r = new Runnable() { public void run() { Callbacks callbacks = mCallbacks.get(); if (callbacks != null) { callbacks.bindAllApplications(list); } } }; mUiExecutor.execute(r); }
后面就是图标缓存的刷新,以及快捷图标的绑定。过程多差不多,这里不再讲解,至此此篇简介结束。