Launcher3主页面加载显示流程分析

布局结构

抓取布局后,可以看到每个图标是一个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和LauncherModelLauncherAppState app = LauncherAppState.getInstance(this);mModel = app.getModel();// 2.infalte布局文件,找到viewsetupViews();// 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 promptmOnInitialBindListener = Boolean.FALSE::booleanValue;}}// 4. 设置view给ActivitysetContentView(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为falseboolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;// 之前没有加载过,bindDirectly为falseboolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0; // bindAllCallbacks为true, 上一步已经addCallbacks了,所以callbacksList里面包含从Launcher.java传进来的callbacksfinal Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;if (callbacksList.length > 0) {// 2.清空PendingBindfor (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 stepList<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 breakwaitForIdle();// second stepList<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 stepList<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();mResults.bindDeepShortcuts();updateHandler.updateIcons(allDeepShortcuts,new ShortcutCachingLogic(), (pkgs, user) -> { });waitForIdle();// fourth stepList<ComponentWithLabelAndIcon> allWidgetsList =mBgDataModel.widgetsModel.update(mApp, null);mResults.bindWidgets();updateHandler.updateIcons(allWidgetsList,new ComponentWithIconCachingLogic(mApp.getContext(), true),mApp.getModel()::onWidgetLabelsUpdated);// fifth steploadFolderNames();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 遍历cursorfinal 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.将数据添加到mBgDataModelc.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 foldersc.markRestored();c.checkAndAddItem(folderInfo, mBgDataModel, logger);break;}} catch (Exception e) {Log.e(TAG, "Desktop items loading interrupted", e);}}} finally {IOUtils.closeSilently(c);}// Load delegate itemsmModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts);// Load string cachemModelDelegate.loadStringCache(mBgDataModel.stringCache);// Remove dead itemsmItemsDeleted = 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 collectionsArrayList<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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/66332.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++编程进阶:标准库中的算法库解析

文章目录 概述1. 非修改性序列操作2. 修改性序列操作3. 排序相关算法4. 二分查找算法5. 合并与集合操作6. 堆操作7. 最小/最大操作8. 数值算法(`<numeric>`头文件)概述 算法库总览:介绍了C++ 标准库提供的海量算法,这些算法作用于各类容器(如vector、list、set等)和…

Express 加 sqlite3 写一个简单博客

例图&#xff1a; 搭建 命令&#xff1a; 前提已装好node.js 开始创建项目结构 npm init -y package.json:{"name": "ex01","version": "1.0.0","main": "index.js","scripts": {"test": &q…

Linux双端口服务器:端口1的文件系统目录挂载到端口2

目录 一、服务器安装NFS服务并配置二、文件挂载三、持久化挂载总结为什么服务器配置多个端口 目前有一台服务器&#xff0c;不过他设置了两个SSH的端口&#xff0c;通过下面方法可以让这两个端口连接的主机能够共享同一个文件系统&#xff0c;原本这两个端口的文件系统是隔离的…

nginx-灰度发布策略(split_clients)

一. 简述&#xff1a; 基于客户端的灰度发布&#xff08;也称为蓝绿部署或金丝雀发布&#xff09;是一种逐步将新版本的服务或应用暴露给部分用户&#xff0c;以确保在出现问题时可以快速回滚并最小化影响的技术。对于 Nginx&#xff0c;可以通过配置和使用不同的模块来实现基于…

【NLP自然语言处理】Transformer模型的几大核心优势与应用前景

目录 &#x1f354; Transformer的并行计算 &#x1f354; Transformer架构的并行化过程 2.1 Transformer架构中Encoder的并行化 2.2 Transformer架构中Decoder的并行化 &#x1f354; Transformer的特征抽取能力 &#x1f354; 为什么说Transformer可以代替seq2seq? 4…

数据结构与算法之排序

9.1 排序的概念 1. 排序的定义 定义&#xff1a;排序是将表中的记录按关键字递增&#xff08;或递减&#xff09;有序排列的过程。说明&#xff1a;数据中可以存在相同关键字的记录。本章主要考虑递增排序。扩展&#xff1a;排序是数据处理中的基本操作之一&#xff0c;广泛应用…

《C++11》各种初始化方式的详细列举与对比

在 C 中&#xff0c;初始化对象的方式多种多样。随着 C 标准的演进&#xff0c;特别是 C11 的引入&#xff0c;初始化方式得到了显著的扩展和改进。本文将详细列举 C 中的各种初始化方式&#xff0c;并对它们进行对比&#xff0c;帮助开发者更好地理解和应用这些特性。 1. C98…

基于 Python Django 的西西家居全屋定制系统(源码+部署+文档)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

25考研|重邮软件工程复试攻略!

与计算机一样&#xff0c;重邮复试不合格也很有可能被淘汰&#xff01;快快认真准备起来&#xff01; 一、复试内容 1、笔试&#xff1a;分值100 2、综合面试&#xff1a;满分100 主要考核考生的综合素质和业务能力&#xff0c;由各招生学院具体组织实施&#xff0c;综合面试…

如何制作重识别数据集及如何解决all query identities do not appear in gallery的问题

如何制作重识别数据集 数据集制作链接 注意点&#xff1a; 按照上述方式制作完成数据集之后&#xff0c;分别建立3个文件夹&#xff0c;分别为train&#xff0c;test&#xff0c;query&#xff0c; 值得注意的是&#xff0c;query文件里的相机编号要进行修改&#xff0c;修改…

链地址法(哈希桶)

链地址法&#xff08;哈希桶&#xff09; 解决冲突的思路 开放定址法中所有的元素都放到哈希表⾥&#xff0c;链地址法中所有的数据不再直接存储在哈希表中&#xff0c;哈希表 中存储⼀个指针&#xff0c;没有数据映射这个位置时&#xff0c;这个指针为空&#xff0c;有多个数…

【C语言】可移植性陷阱与缺陷(七): 除法运算时发生的截断

在C语言编程中&#xff0c;除法运算可能会引发一些与可移植性相关的问题&#xff0c;特别是当涉及到整数除法时发生的截断&#xff08;truncation&#xff09;。不同平台对于整数除法的行为和处理方式可能会有所不同&#xff0c;这可能导致代码在不同编译器或硬件平台上的行为不…

了解RabbitMQ的工作原理

RabbitMQ是一个开源的消息代理系统&#xff0c;实现了高级消息队列协议&#xff08;AMQP&#xff09;。在现代分布式系统中&#xff0c;特别是在微服务架构中&#xff0c;RabbitMQ有广泛的应用。本文将详细介绍RabbitMQ的工作原理&#xff0c;并通过实践案例帮助读者理解和应用…

分布式搜索引擎之elasticsearch基本使用3

分布式搜索引擎之elasticsearch基本使用3 1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; docker network create es-net1.2.加载镜像 这里我们采用elasticsearch的7.12.1版本的镜像&…

【FlutterDart】 拖动改变 widget 的窗口尺寸大小GestureDetector~简单实现(10 /100)

上效果 预期的是通过拖动一条边界线改变窗口大小&#xff0c;类似vscode里拖动效果。这个是简单的拖动实现 上代码&#xff1a; import package:flutter/material.dart;class MyDraggableViewDemo extends StatelessWidget {const MyDraggableViewDemo({super.key});override…

使用Dinky快速提交Flink operator任务

官网地址&#xff1a;K8s集成 | Dinky 1.目前使用版本 Dinky1.2.0、Flink1.18.1、Flink operator0.10.0 2.制作镜像 2.1创建DockerFile ARG FLINK_VERSION1.18.1 FROM flink:${FLINK_VERSION}-scala_2.12 RUN mkdir -p /opt/flink/usrlib COPY commons-cli-1.3.1.jar …

查找路由器的管理后台ip【通用找IP】

需求&#xff1a; 刚刚搞了个【小米】路由器&#xff0c;我想进路由的管理后台&#xff0c;提示&#xff1a;安装xx的路由管家&#xff0c;我不想安装 但是无法找到这个管理后台。 而且我是用这个路由作为中继&#xff0c;那么这个路由的ip就会经常更换 尝试通过网上搜索引擎来…

【大数据】(选修)实验4 安装熟悉HBase数据库并实践

实验4 安装熟悉HBase数据库并实践 1、实验目的 (1)理解HBase在Hadoop体系结构中的角色; (2)熟练使用HBase操作常用的Shell命令; (3)熟悉HBase操作常用的Java API。 2、实验平台 操作系统:Linux Hadoop版本:2.6.0或以上版本 HBase版本:1.1.2或以上版本 JDK版…

【通识安全】煤气中毒急救的处置

1.煤气中毒的主要症状与体征一氧化碳中毒&#xff0c;其中毒症状一般分为轻、中、重三种。 (1)轻度&#xff1a;仅有头晕、头痛、眼花、心慌、胸闷、恶心等症状。如迅速打开门窗&#xff0c;或将病人移出中毒环境&#xff0c;使之吸入新鲜空气和休息&#xff0c;给些热饮料&am…

【UI自动化测试】selenium八种定位方式

&#x1f3e1;个人主页&#xff1a;謬熙&#xff0c;欢迎各位大佬到访❤️❤️❤️~ &#x1f472;个人简介&#xff1a;本人编程小白&#xff0c;正在学习互联网求职知识…… 如果您觉得本文对您有帮助的话&#xff0c;记得点赞&#x1f44d;、收藏⭐️、评论&#x1f4ac;&am…