【Android】Android Framework系列--Launcher3桌面图标加载流程

news2025/6/9 8:52:32

Launcher3桌面加载流程

Android Launcher3(简称Launcher)启动后会加载桌面。基于Android12代码,分析一下桌面加载的流程。
一些相关的概念:

  • WorkSpace:桌面。在桌面上可以添加快捷方式、Hoseat或Dock(就是手机或者车机系统在桌面底部的图标栏)、Widet小组件(比如天气)等。
  • AllApp:App List,呈现所有App。点击任意App图标可以启动该App。
  • DeepShortcuts: 桌面上的应用快捷方式。
  • Widget:小组件,一般添加到桌面上,比如天气、闹钟、股票之类。

Launcher桌面加载

Launcher被Android AMS拉起后,进入自己的生命流程。Launcher.java 中的onCreate函数被调用,准备开始加载桌面。

//packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
@Override
protected void onCreate(Bundle savedInstanceState) {
	Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
			TraceHelper.FLAG_UI_EVENT);
	LauncherAppState app = LauncherAppState.getInstance(this);
	mOldConfig = new Configuration(getResources().getConfiguration());
	mModel = app.getModel();

	if (!mModel.addCallbacksAndLoad(this)) {
		// 省略
	}
}

addCallbacksAndLoad在LauncherModel.java中实现,在这个函数中调用了startLoader函数,该函数中会创建LoaderResults对象。如果是首次启动情况下,调用函数startLoaderForResults,在startLoaderForResults函数中创建LoaderTask并利用之前创建的LoaderResults开始加载桌面。

//packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java
/**
 * Adds a callbacks to receive model updates
 * @return true if workspace load was performed synchronously
 */
public boolean addCallbacksAndLoad(Callbacks callbacks) {
    synchronized (mLock) {
        addCallbacks(callbacks);
        return startLoader();

    }
}

/**
 * Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible.
 * @return true if the page could be bound synchronously.
 */
public boolean startLoader() {
	ItemInstallQueue.INSTANCE.get(mApp.getContext())
			.pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
	synchronized (mLock) {
		final Callbacks[] callbacksList = getCallbacks();
		if (callbacksList.length > 0) {
			for (Callbacks cb : callbacksList) {
				MAIN_EXECUTOR.execute(cb::clearPendingBinds);
			}
			
			//  如果直接在加载了,请停止掉。
			// If there is already one running, tell it to stop.
			stopLoader();
			LoaderResults loaderResults = new LoaderResults(
					mApp, mBgDataModel, mBgAllAppsList, callbacksList);
			if (mModelLoaded && !mIsLoaderTaskRunning) {
				// 非首次启动。Launcher直接从数据库中同步加载。
				loaderResults.bindWorkspace();
				loaderResults.bindAllApps();
				loaderResults.bindDeepShortcuts();
				loaderResults.bindWidgets();
				return true;
			} else {
				// 首次启动走这里。
				startLoaderForResults(loaderResults);
			}
		}
	}
	return false;
}

public void startLoaderForResults(LoaderResults results) {
     synchronized (mLock) {
         stopLoader();
         // 创建LoaderTask,通过loadTask加载桌面。
         mLoaderTask = new LoaderTask(
                 mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, results);
         MODEL_EXECUTOR.post(mLoaderTask);
     }
 }

从startLoader这个函数中,可以看出来。Launcher启动时加载的流程是:

  1. Workspace
  2. AllApps
  3. DeepShortcuts
  4. Widgets
    因为Workspace(直观上就是用户看到的桌面)是第一个呈现给用户的,并且桌面也是快捷方式、Widget的容器,所以肯定会第一个加载。
    接下来,LoaderTask被执行,调用其run函数。
//packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java
public void run() {
	synchronized (this) {
		// 退出阶段,直接返回
		// Skip fast if we are already stopped.
		if (mStopped) {
			return;
		}
	}

	try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
		//顺序也是先加载 workspace,然后AllApp,然后DeepShortcuts,然后Widgets
		List<ShortcutInfo> allShortcuts = new ArrayList<>();
		// 加载WorkSpace数据
		loadWorkspace(allShortcuts);
		logASplit(logger, "loadWorkspace");

		verifyNotStopped();
		// 绑定WorkSpace数据(绑定图标之类的数据,桌面及其上内容开始呈现)
		mResults.bindWorkspace();
		logASplit(logger, "bindWorkspace");

		mModelDelegate.workspaceLoadComplete();
		// Notify the installer packages of packages with active installs on the first screen.
		// 发送第一个Screen(开机后默认显示的第一个Screen,如果图标多的话,会被分成多个Screen,通过左右滑动显示其他的)
		sendFirstScreenActiveInstallsBroadcast();
		logASplit(logger, "sendFirstScreenActiveInstallsBroadcast");

		// Take a break
		waitForIdle();
		logASplit(logger, "step 1 complete");
		verifyNotStopped();

		// second step
		List<LauncherActivityInfo> allActivityList = loadAllApps();
		logASplit(logger, "loadAllApps");

		verifyNotStopped();
		mResults.bindAllApps();
		logASplit(logger, "bindAllApps");
		// 省略

		// Take a break
		waitForIdle();
		logASplit(logger, "step 2 complete");
		verifyNotStopped();

		// third step
		List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();
		logASplit(logger, "loadDeepShortcuts");

		verifyNotStopped();
		mResults.bindDeepShortcuts();
		logASplit(logger, "bindDeepShortcuts");

		// Take a break
		waitForIdle();
		logASplit(logger, "step 3 complete");
		verifyNotStopped();

		// fourth step
		List<ComponentWithLabelAndIcon> allWidgetsList =
				mBgDataModel.widgetsModel.update(mApp, null);
		logASplit(logger, "load widgets");

		verifyNotStopped();
		mResults.bindWidgets();
		logASplit(logger, "bindWidgets");
		verifyNotStopped();

		mModelDelegate.modelLoadComplete();
		transaction.commit();
	} 
}

上面的代码中,开始加载Launcher中的workspace、allapp、deepshortcut、Widget。先加载其数据,然后一步步绑定这些数据(桌面上就开始呈现出内容)。因为代码比较多且流程相似,这里主要关注WorkSpace的加载。

//packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java
private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
	loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI,
			null /* selection */);
}

protected void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, Uri contentUri,
		String selection) {

	boolean clearDb = false;
	// 省略

	if (clearDb) {
		Log.d(TAG, "loadWorkspace: resetting launcher database");
		LauncherSettings.Settings.call(contentResolver,
				LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
	}

	Log.d(TAG, "loadWorkspace: loading default favorites");
	LauncherSettings.Settings.call(contentResolver,
			LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);

	synchronized (mBgDataModel) {
		final HashMap<PackageUserKey, SessionInfo> installingPkgs =
				mSessionHelper.getActiveSessions();
		installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
		mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
		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 {
			// 省略
			while (!mStopped && c.moveToNext()) {
				// 循环刚刚创建的DB文件。读取DB中的信息(呈现哪些应用图标之类)
				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(比如快捷方式对应的intent)
						intent = c.parseIntent();

						// If it's a deep shortcut, we'll use pinned shortcuts to restore it
						if (cn != null && validTarget && c.itemType
								!= LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
							// If the component is already present
							// 如果应用没有被Disable掉,那就么会显示它(通过pm可以disable应用)
							if (mLauncherApps.isActivityEnabled(cn, c.user)) {
								// no special handling necessary for this item
								c.markRestored();
							} else {
								//否则不会显示这个应用。
								// Gracefully try to find a fallback activity.
								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;
								}
							}
						}
						// 省略,后面就是从数据库遍历出数据,缓存到各个对象中了(内存中)
						break;
						// 省略
					}
				} catch (Exception e) {
					Log.e(TAG, "Desktop items loading interrupted", e);
				}
			}

		// Load delegate items
		mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts);

		// Break early if we've stopped loading
		if (mStopped) {
			mBgDataModel.clear();
			return;
		}

		// Remove dead items
		mItemsDeleted = c.commitDeleted();
	}
}

loadWorkspace函数中通过LauncherSettings创建了Launcher中的数据。并加载了默认的布局数据到 创建的DB中。然后遍历DB,将数据赋给对应的对象。初次启动时,加载默认布局数据,会按如下顺序进行:

  1. 找launcher3.layout.provider这个key对应的value(contentprovider),然后通过这个value值读取到配置的launcher_layout的信息。
  2. 如果第一步没找到。那么找系统中包含“android.autoinstalls.config.action.PLAY_AUTO_INSTALL”的应用,通过它获取launcher_layout信息。
  3. 如果第二步没找到。找系统中com.android.launcher3.action.PARTNER_CUSTOMIZATION对应的应用,通过它获取launcher_layout信息。
  4. 如果第三步没找到。加载Launcher中默认的workspace布局( /packages/apps/Launcher3/res/xml/这个目录下的default_workspace_*.xml文件)

关于查找默认布局的实现,可以参考LauncherProvider中的loadDefaultFavoritesIfNecessary函数。
在这里插入图片描述
到此,Launcher桌面需要的数据加载完成。下面将数据绑定(显示出来)

Launcher桌面数据绑定

回到LauncherTask的run函数中loadWorkspace函数执行完成后,调用LoaderResultsbindWorkspace函数完成WorkSpace的数据绑定。绑定数据后,后面Activity渲染时就会用这些数据呈现出桌面上的元素。

//packages/apps/Launcher3/src/com/android/launcher3/model/LoaderTask.java
public void run() {
	synchronized (this) {
		// Skip fast if we are already stopped.
		if (mStopped) {
			return;
		}
	}

	Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
	TimingLogger logger = new TimingLogger(TAG, "run");
	try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
		List<ShortcutInfo> allShortcuts = new ArrayList<>();
		loadWorkspace(allShortcuts);
		logASplit(logger, "loadWorkspace");


		verifyNotStopped();
		// 绑定WorkSpace中的数据
		mResults.bindWorkspace();
		logASplit(logger, "bindWorkspace");
}

LoaderResults的bindWorkspace函数,在其父类BaseLoaderResults中定义。该函数中,创建workspaceItems、appWidgets、orderedScreenIds (屏幕数)等信息的数组。然后创建WorkspaceBinder,调用其bind函数开始绑定。

//packages/apps/Launcher3/src/com/android/launcher3/model/BaseLoaderResults.java
public void bindWorkspace() {
	// 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);
		mBgDataModel.lastBindId++;
		mMyBindingId = mBgDataModel.lastBindId;
	}

	for (Callbacks cb : mCallbacksList) {
		// callback对象是Launcher这个类
		new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
				workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
	}
}

WorkspaceBinder的bind函数中,首先拿到当前屏幕(就是呈现给用户的第一个屏幕)ID,然后优先往第一个屏幕上绑定内容。之后再绑定其他屏幕的内容。

private void bind() {
	final int currentScreen;
	{
		// Create an anonymous scope to calculate currentScreen as it has to be a
		// final variable.
		int currScreen = mCallbacks.getPageToBindSynchronously();
		if (currScreen >= mOrderedScreenIds.size()) {
			// There may be no workspace screens (just hotseat items and an empty page).
			currScreen = PagedView.INVALID_PAGE;
		}
		currentScreen = currScreen;
	}
	final boolean validFirstPage = currentScreen >= 0;
	// 拿到当前的屏幕ID
	final int currentScreenId =
			validFirstPage ? mOrderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;

	// Separate the items that are on the current screen, and all the other remaining items
	ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
	ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
	ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
	ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();

	filterCurrentWorkspaceItems(currentScreenId, mWorkspaceItems, currentWorkspaceItems,
			otherWorkspaceItems);
	filterCurrentWorkspaceItems(currentScreenId, mAppWidgets, currentAppWidgets,
			otherAppWidgets);
	final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
	sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
	sortWorkspaceItemsSpatially(idp, otherWorkspaceItems);

	// Tell the workspace that we're about to start binding items
	executeCallbacksTask(c -> {
		c.clearPendingBinds();
		c.startBinding();
	}, mUiExecutor);

	// Bind workspace screens
	// 先绑定屏幕
	executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);

	Executor mainExecutor = mUiExecutor;
	// Load items on the current page.
	// 往当前的屏幕上绑定数据内容
	bindWorkspaceItems(currentWorkspaceItems, mainExecutor);
	bindAppWidgets(currentAppWidgets, mainExecutor);
	mExtraItems.forEach(item ->
			executeCallbacksTask(c -> c.bindExtraContainerItems(item), 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() : mainExecutor;

	executeCallbacksTask(c -> c.finishFirstPageBind(
			validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null), mainExecutor);
	// 绑定非当前屏幕上的内容
	bindWorkspaceItems(otherWorkspaceItems, deferredExecutor);
	bindAppWidgets(otherAppWidgets, deferredExecutor);
	// Tell the workspace that we're done binding items
	executeCallbacksTask(c -> c.finishBindingItems(currentScreen), deferredExecutor);

	if (validFirstPage) {
		executeCallbacksTask(c -> {
			// We are loading synchronously, which means, some of the pages will be
			// bound after first draw. Inform the mCallbacks that page binding is
			// not complete, and schedule the remaining pages.
			c.onPageBoundSynchronously(currentScreen);
			c.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);

		}, mUiExecutor);
	}
}

通过调用Launcher类的bindScreens函数,绑定屏幕(添加屏幕)后,调用bindWorkspaceItemsbindAppWidgets等函数往屏幕上绑定数据。实际上这些函数,最终会调用Launcher类中的bindItems,根据图标信息创建View并addView,并且给各个View设置其TouchListener。感兴趣的可以顺着这些函数继续看下。
另外Launcher中的数据库(就是上面首次启动时创建的空数据库,并加载了布局数据。一般名称为Launcher.db)用于保存桌面相关数据信息,其创建在LauncherProvider中实现。
在这里插入图片描述

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

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

相关文章

k8s(三): 基本概念-ReplicaSet与Deployment

PeplicaSet ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合&#xff0c;通常用来保证给定数量的、完全相同的 Pod 的可用性。 最佳实践 Deployment 是一个可以拥有 ReplicaSet 并使用声明式方式在服务器端完成对 Pod 滚动更新的对象。 尽管 Rep…

链式队列的结构设计及基本操作的实现(初始化,入队,出队,获取元素个数,判空,清空,销毁)

目录 一.链式队列的设计思想 二.链式队列的结构设计 三.链式队列的实现 四.链式队列的总结 一.链式队列的设计思想 首先一定要理解设计的初衷,就是队头队尾的位置要满足怎么快怎么设计.那么分析如下: 最终我们敲定了入队,出队的时间复杂度都为O(1)的一种设计,也就是第四种设…

7、Jenkins+Nexus3+Docker+K8s实现CICD

文章目录 基本环境配置一、Jenkins安装必要插件二、Jenkins系统配置三、新建流水线四、在项目工程里添加Jenkinsfile、deploy.yml五、在项目工程里添加Dockerfile在这里插入图片描述 总结 提示&#xff1a;本章主要记录各基本环境搭建好后如何配置Jenkins流水线部署微服务到K8s…

【动态规划】LeetCode-63.不同路径II

&#x1f388;算法那些事专栏说明&#xff1a;这是一个记录刷题日常的专栏&#xff0c;每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目&#xff0c;在这立下Flag&#x1f6a9; &#x1f3e0;个人主页&#xff1a;Jammingpro &#x1f4d5;专栏链接&…

探索未来能源:可控核聚变的挑战与希望

探索未来能源:可控核聚变的挑战与希望 引言 随着人类社会的不断发展,对能源的需求也在持续增长。传统的化石燃料能源在燃烧过程中会产生大量的二氧化碳和其他温室气体,导致全球气候变暖,对环境产生了重大威胁。因此,寻找一种清洁、可持续、高效的能源成为了当务之急。在…

MMseqs2蛋白质序列快速高效比对工具

先看仓库&#xff1a;soedinglab/MMseqs2: MMseqs2: ultra fast and sensitive search and clustering suite (github.com) 无论哪个工具软件&#xff0c;无论你是否熟悉&#xff0c;都推荐你看一下作者原文&#xff0c;这样后面的步骤以及怎么使用头脑里会更清晰。 Fast an…

Linux expect命令详解

在Linux系统中&#xff0c;expect 是一款非常有用的工具&#xff0c;它允许用户自动化与需要用户输入进行交互的程序。本文将深入探讨expect命令的基本语法、使用方法以及一些最佳实践。 什么是Expect命令&#xff1f; expect 是一个用于自动化交互式进程的工具。它的主要功能…

VSC++=》 美丽字符串()

缘由https://bbs.csdn.net/topics/396428505 void 美丽字符串() {string zf ""; int j 0, b 0, sl[47]{}, wi 0;cin >> zf; zf "0";// zf.push_back(0);while (zf[j] ! 0)if (zf[b] zf[j])sl[b], b 0, j; else b;//if ((wi zf.find(zf[j]))…

Linux CentOS7 联网配置 | 安装中文输入法

参考视频&#xff1a;保姆式教学虚拟机联网liunx(centos)_哔哩哔哩_bilibili 配置网络&#xff1a;解决上网问题 第一步&#xff1a;选择网络模式 第二步&#xff1a;配置网卡命令&#xff1a;打开终端执行命令&#xff1a; 1、先切换到根目录下&#xff0c;防止在第执行cd …

【Linux】ubuntu配置SSH服务

要在Ubuntu上配置SSH服务&#xff0c;首先安装ssh-server sudo apt install openssh-server 安装完成后&#xff0c;可以检查一下是否安装成功 systemctl status ssh vim /etc/ssh/sshd_config 此时ubuntu就可以被远程连接工具连接了&#xff0c;如果我们想配置关于SCP服务允…

基于HTML+CSS+JavaScript的登录注册网页设计

一、界面效果: 二、HTML代码: 登录注册html: 登录成功html: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <h1>登录成功!</h1> </body> <…

【计算机网络学习之路】URL概念及组成

目录 一. URL是什么 二. URL的组成 三. encode和decode 前言 本系列文章是计算机网络学习的笔记&#xff0c;欢迎大佬们阅读&#xff0c;纠错&#xff0c;分享相关知识。希望可以与你共同进步。 本篇讲解使用浏览器不可或缺的部分——URL 一. URL是什么 域名及DNS 我们在…

Azure Machine Learning - 在 Azure AI 搜索中创建全文查询

Azure AI搜索中如果要为全文搜索生成查询&#xff0c;本文提供了设置请求的步骤。 本文还介绍了查询结构&#xff0c;并说明了字段属性和语言分析器如何影响查询结果。 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&a…

口罩标准讲解及推荐

一、我国医用口罩质量标准 1、YY/T0969-2013《一次性使用医用口罩》 2、YY0469-2011《医用外科口罩》 3、GB19083-2010《医用防护口罩技术要求》三类医用口罩各项指标对比如下&#xff1a; 结论&#xff1a;防护效果来说&#xff1a;医用防护口罩>医用外科口罩>一次性…

wordpress忘记密码怎么办?

有的时候&#xff0c;我们会忘记网站的密码&#xff0c;所以网站的密码要记住&#xff0c;那记不住&#xff0c;怎么样才可以登录后台呢&#xff1f;下面来给大家说一下方法&#xff0c;第一种方法&#xff0c;就是进入数据库里面修改密码&#xff0c;第二种就是从新搭建&#…

对Spring框架的一些总结

对Spring框架的一些总结 在文章开头我真心推荐大家一个优秀的编程老师&#xff1a;孙帅老师(孙哥suns)&#xff0c;孙帅老师在哔哩哔哩的Spring5教学视频时长接近33个小时&#xff0c;从0基础到一步一步手把手的教你抽丝剥茧分析Spring框架的所有知识&#xff0c;孙帅老师的教…

基于单片机智能电子秤称重系统设计

**单片机设计介绍&#xff0c;基于单片机智能电子秤称重系统设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的智能电子秤称重系统是一种通过使用单片机、载荷传感器等组件来实现精准称重的系统。 该系统的设计流程如下&…

Java基础-----Math类相关的API(一)

文章目录 1. Math类1.1 简介1.2 常用方法1.3 小练身手 2.Random类2.1 简介2.2 构造方法2.3 常用方法 3. BigInteger和BigDecimal3.1 简介3.2 创建方式3.3 方法3.4 RoundingModel处理方式(8种)3.4.1 **UP**3.4.2 **DOWN**3.4.3 **CEILING**3.4.4 **FLOOR**3.4.5 **HALF_UP**3.4.…

神经网络(第三周)

一、简介 1.1 非线性激活函数 1.1.1 tanh激活函数 使用一个神经网络时&#xff0c;需要决定在隐藏层上使用哪种激活函数&#xff0c;哪种用在输出层节点上。到目前为止&#xff0c;只用过sigmoid激活函数&#xff0c;但是&#xff0c;有时其他的激活函数效果会更好。tanh函数…

18487.1 - 2015 电动汽车充电系统标准 第1部分 关键点梳理

一、部分知识介绍 1、连接方式 使用电缆和连接器将电动汽车接入电网&#xff08;电源&#xff09;的方法。 1.1、连接方式A 1.2、连接方式B 1.3、连接方式C 2、电动汽车控电设备 2.1、按照输出电压分类 1&#xff09;交流 单相 220V&#xff0c;三相 380V. 2&#xff09…