布局布局
抓取布局后,可以看到每个图标是一个DoubleShadowBubbleTextView,父布局是CellLayout、workspace。
我们可以在CellLayout添加子view打印出调用堆栈信息,可以整体上看页面加载显示流程。
重要类
- Launcher.java:主界面,即MainActivity
- launcher.xml:主界面布局文件
- LauncherModel.java:管理Launcher状态,包罗加载使命、状态回调等
- LoaderTask.java:加载使命,是一个Runnable
- LoaderResults.java:加载结果
- BgDataModel#Callbacks:数据回调接口,Launcher实现该接口,加载使命通过该接口回调给Launcher
- LauncherProvider:桌面数据提供者,采用db生存桌面图标数据(包罗排列位置、范例等)
- LauncherSettings:封装访问LauncherProvider时的uri、column等一些常量,通过ContentResolver来访问LauncherProvider,不直接操作db
- LoaderCursor:封装cursor操作
流程图
创建Activity
主界面创建的时间,通例的setContentView和findView,创建LauncherModel并开始加载数据
- // Launcher.java
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // 1.创建LauncherAppState和LauncherModel
- LauncherAppState app = LauncherAppState.getInstance(this);
- mModel = app.getModel();
-
- // 2.infalte布局文件,找到view
- setupViews();
-
- // 3. 设置loader监听,开始加载数据,加载完成后回调给主界面
- if (!mModel.addCallbacksAndLoad(this)) {
- if (!internalStateHandled) {
- // If we are not binding synchronously, pause drawing until initial bind complete,
- // so that the system could continue to show the device loading prompt
- mOnInitialBindListener = Boolean.FALSE::booleanValue;
- }
- }
-
- // 4. 设置view给Activity
- setContentView(getRootView());
复制代码 加载数据
创建加载使命
mModel.addCallbacksAndLoad将Launcher设置给LauncherModel,然后创建了加载使命
- // LauncherModel.java
- /**
- * Adds a callbacks to receive model updates
- * @return true if workspace load was performed synchronously
- */
- public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
- synchronized (mLock) {
- addCallbacks(callbacks);
- return startLoader(new Callbacks[] { callbacks });
- }
- }
- private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
- synchronized (mLock) {
- // 1.取消旧的加载任务
- boolean wasRunning = stopLoader(); // 之前没有loader任务,wasRunning为false
- boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;// 之前没有加载过,bindDirectly为false
- boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0; // bindAllCallbacks为true, 上一步已经addCallbacks了,所以callbacksList里面包含从Launcher.java传进来的callbacks
- final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;
- if (callbacksList.length > 0) {
- // 2.清空PendingBind
- for (Callbacks cb : callbacksList) {
- MAIN_EXECUTOR.execute(cb::clearPendingBinds);
- }
- // 3.创建LoaderResults和LoaderTask,开始加载数据
- LoaderResults loaderResults = new LoaderResults(
- mApp, mBgDataModel, mBgAllAppsList, callbacksList);
- if (bindDirectly) {
- loaderResults.bindWorkspace(bindAllCallbacks);
- loaderResults.bindAllApps();
- loaderResults.bindDeepShortcuts();
- loaderResults.bindWidgets();
- return true;
- } else {
- stopLoader();
- mLoaderTask = new LoaderTask(
- mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
- MODEL_EXECUTOR.post(mLoaderTask);
- }
- }
- }
- }
复制代码 整体加载步调
LoaderTask#run中根据范例加载步调分为了5步,图标的加载重要看第一步
- // LoaderTask.java
- public void run() {
- try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
- // first step
- List<ShortcutInfo> allShortcuts = new ArrayList<>();
- loadWorkspace(allShortcuts, memoryLogger);
- // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
- // sanitizeData should not be invoked if the workspace is loaded from a db different
- // from the main db as defined in the invariant device profile.
- // (e.g. both grid preview and minimal device mode uses a different db)
- if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
- sanitizeData();
- }
- mResults.bindWorkspace(true /* incrementBindId */);
- mModelDelegate.workspaceLoadComplete();
- // Notify the installer packages of packages with active installs on the first screen.
- sendFirstScreenActiveInstallsBroadcast();
- // Take a break
- waitForIdle();
- // second step
- List<LauncherActivityInfo> allActivityList;
- allActivityList = loadAllApps();
- mResults.bindAllApps();
- IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
- setIgnorePackages(updateHandler);
- updateHandler.updateIcons(allActivityList,
- LauncherActivityCachingLogic.newInstance(mApp.getContext()),
- mApp.getModel()::onPackageIconsUpdated);
- updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
- mApp.getModel()::onPackageIconsUpdated);
- waitForIdle();
- // third step
- List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
- mResults.bindDeepShortcuts();
- updateHandler.updateIcons(allDeepShortcuts,
- new ShortcutCachingLogic(), (pkgs, user) -> { });
- waitForIdle();
-
- // fourth step
- List<ComponentWithLabelAndIcon> allWidgetsList =
- mBgDataModel.widgetsModel.update(mApp, null);
- mResults.bindWidgets();
- updateHandler.updateIcons(allWidgetsList,
- new ComponentWithIconCachingLogic(mApp.getContext(), true),
- mApp.getModel()::onWidgetLabelsUpdated);
-
- // fifth step
- loadFolderNames();
- updateHandler.finish();
- mModelDelegate.modelLoadComplete();
- transaction.commit();
- } catch (CancellationException e) {
- ...
- }
- }
复制代码 图标解析
loadWorkspace()代码比较多,重要作用是通过ContentResolver查询LauncherProvider中生存的桌面图标信息,然后遍历cursor来解析数据。
在分析代码的时间,抓到数据流向,可以结合真实的db数据举行分析,其中一些判断容错处置惩罚可以跳过
重要流程如下:
- 加载默认的数据(Provider会有判断,只有第一次才会加载)
- 通过ContentResolver查询全部数据
- 遍历cursor,根据不同图标范例举行解析,如ITEM_TYPE_APPLICATION代表普通的图标,ITEM_TYPE_FOLDER代表文件夹
- 对数据举行一些校验,校验通事后将数据添加到mBgDataModel
数据流向:
Db(LauncherProvider) --> Cursor(LoaderCursor) --> List(BgDataModel)
- protected void loadWorkspace(
- List<ShortcutInfo> allDeepShortcuts,
- Uri contentUri,
- String selection,
- @Nullable LoaderMemoryLogger logger) {
- final Context context = mApp.getContext();
- final ContentResolver contentResolver = context.getContentResolver();
- // 1. 加载默认的数据(Provider会有判断,只有第一次才会加载)
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
- synchronized (mBgDataModel) {
- mBgDataModel.clear();
- mPendingPackages.clear();
- // 2. 查询所有数据
- final LoaderCursor c = new LoaderCursor(
- contentResolver.query(contentUri, null, selection, null, null), contentUri,
- mApp, mUserManagerState);
- final Bundle extras = c.getExtras();
- mDbName = extras == null
- ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME);
- try {
- // 2.1 遍历cursor
- 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 int sourceContainerIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.APPWIDGET_SOURCE);
- WorkspaceItemInfo info;
- LauncherAppWidgetInfo appWidgetInfo;
- LauncherAppWidgetProviderInfo widgetProviderInfo;
- Intent intent;
- String targetPkg;
- List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
- while (!mStopped && c.moveToNext()) {
- try {
- // 2.2 根据不同图标类型进行解析,ITEM_TYPE_APPLICATION代表普通的图标,ITEM_TYPE_FOLDER代表文件夹
- 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();
- // 2.3 解析数据,进行一些校验判断
- if (info != null) {
- if (info.itemType
- != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- // Skip deep shortcuts; their title and icons have already been
- // loaded above.
- iconRequestInfos.add(
- c.createIconRequestInfo(info, useLowResIcon));
- }
- c.applyCommonProperties(info);
- info.intent = intent;
- info.rank = c.getInt(rankIndex);
- info.spanX = 1;
- info.spanY = 1;
- info.runtimeStatusFlags |= disabledState;
- if (isSafeMode && !isSystemApp(context, intent)) {
- info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
- }
- LauncherActivityInfo activityInfo = c.getLauncherActivityInfo();
- if (activityInfo != null) {
- info.setProgressLevel(
- PackageManagerHelper
- .getLoadingProgress(activityInfo),
- PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
- }
- if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
- tempPackageKey.update(targetPkg, c.user);
- SessionInfo si = installingPkgs.get(tempPackageKey);
- if (si == null) {
- info.runtimeStatusFlags &=
- ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
- } else if (activityInfo == null) {
- int installProgress = (int) (si.getProgress() * 100);
- info.setProgressLevel(
- installProgress,
- PackageInstallInfo.STATUS_INSTALLING);
- }
- }
- // 3.将数据添加到mBgDataModel
- c.checkAndAddItem(info, mBgDataModel, logger);
- } else {
- throw new RuntimeException("Unexpected null WorkspaceItemInfo");
- }
- 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, logger);
- break;
- }
- } catch (Exception e) {
- Log.e(TAG, "Desktop items loading interrupted", e);
- }
- }
- } finally {
- IOUtils.closeSilently(c);
- }
- // Load delegate items
- mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts);
- // Load string cache
- mModelDelegate.loadStringCache(mBgDataModel.stringCache);
- // Remove dead items
- mItemsDeleted = c.commitDeleted();
- // Sort the folder items, update ranks, and make sure all preview items are high res.
- FolderGridOrganizer verifier =
- new FolderGridOrganizer(mApp.getInvariantDeviceProfile());
- for (FolderInfo folder : mBgDataModel.folders) {
- Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
- verifier.setFolderInfo(folder);
- int size = folder.contents.size();
- // Update ranks here to ensure there are no gaps caused by removed folder items.
- // Ranks are the source of truth for folder items, so cellX and cellY can be ignored
- // for now. Database will be updated once user manually modifies folder.
- for (int rank = 0; rank < size; ++rank) {
- WorkspaceItemInfo info = folder.contents.get(rank);
- info.rank = rank;
- if (info.usingLowResIcon()
- && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && verifier.isItemInPreview(info.rank)) {
- mIconCache.getTitleAndIcon(info, false);
- }
- }
- }
- c.commitRestoredItems();
- }
- }
复制代码 可以结合db数据举行分析:
- container: 表示显示的布局,像普通、文件夹、dock栏等
- screen: 表示现在第几屏
- itemType:表示范例,像应用图标、文件夹、快捷方式等
图标显示
到这里,我们已经获取到桌面数据了,下面就是要设置到view显示出来。在上面LoaderTask#run()中,loadWorkspace()获取到了数据,而ui显示的触发在mResults.bindWorkspace(true)
bindWorkspace()先是将数据复制一份,然后遍历callback举行bind。上一步mBgDataModel生存了数据库中数据,mBgDataModel是LauncherModel的一个成员变量,在activity#onCreate时创建的,而activity就是Callbacks,所以到这里都关联了起来。
Launcher (Callbacks)—> LoaderTask —> LoaderResults --> BgDataModel --> Callbacks
- // LoaderResults.java
- /**
- * Binds all loaded data to actual views on the main thread.
- */
- public void bindWorkspace(boolean incrementBindId) {
- // Save a copy of all the bg-thread collections
- ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
- final IntArray orderedScreenIds = new IntArray();
- ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
- synchronized (mBgDataModel) {
- workspaceItems.addAll(mBgDataModel.workspaceItems);
- appWidgets.addAll(mBgDataModel.appWidgets);
- orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
- mBgDataModel.extraItems.forEach(extraItems::add);
- if (incrementBindId) {
- mBgDataModel.lastBindId++;
- }
- mMyBindingId = mBgDataModel.lastBindId;
- }
- for (Callbacks cb : mCallbacksList) {
- new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
- workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
- }
- }
复制代码 WorkspaceBinder中bind方法会执行callback回调,回调到activity后,创建BubbleTextView添加到CellLayout。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |