Android14 - Framework- Configuration的创建和更新

本文描述启动一个新进程ActivityFramworkConfiguration创建传导过程

首先我们知道所有Window容器继承于WindowContainerWindowContainer本身ConfigurationContainer子类于此同时WindowProcessController也是ConfigurationContainer子类ConfigurationContainer认为可以Window进程进行Configuration配置基本载体单位

我们启动一个Activity角度出发Configuration的传导过程

启动Activity过程调用ActivityTaskSupervisorrealStartActivityLocked方法

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java

boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
	boolean andResume, boolean checkConfig) throws RemoteException {
    ...
    if (checkConfig) {
		// Deferring resume here because we're going to launch new activity shortly.
		// We don't want to perform a redundant launch of the same record while ensuring
		// configurations and trying to resume top activity of focused root task.
		mRootWindowContainer.ensureVisibilityAndConfig(r, r.getDisplayId(),
				false /* markFrozenIfConfigChanged */, true /* deferResume */);
	}
    ...
}

Activity启动场景checkConfigtrue

看ensureVisibilityAndConfig

platform/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
		boolean markFrozenIfConfigChanged, boolean deferResume) {
	// First ensure visibility without updating the config just yet. We need this to know what
	// activities are affecting configuration now.
	// Passing null here for 'starting' param value, so that visibility of actual starting
	// activity will be properly updated.
	ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
			false /* preserveWindows */, false /* notifyClients */);

	if (displayId == INVALID_DISPLAY) {
		// The caller didn't provide a valid display id, skip updating config.
		return true;
	}

	// Force-update the orientation from the WindowManager, since we need the true configuration
	// to send to the client now.
	final DisplayContent displayContent = getDisplayContent(displayId);
	Configuration config = null;
	if (displayContent != null) {
		config = displayContent.updateOrientation(starting, true /* forceUpdate */);
	}
	// Visibilities may change so let the starting activity have a chance to report. Can't do it
	// when visibility is changed in each AppWindowToken because it may trigger wrong
	// configuration push because the visibility of some activities may not be updated yet.
	if (starting != null) {
		starting.reportDescendantOrientationChangeIfNeeded();
	}
	if (starting != null && markFrozenIfConfigChanged && config != null) {
		starting.frozenBeforeDestroy = true;
	}

	if (displayContent != null) {
		// Update the configuration of the activities on the display.
		return displayContent.updateDisplayOverrideConfigurationLocked(config, starting,
				deferResume, null /* result */);
	} else {
		return true;
	}
}

调用了DisplayContentupdateOrientation注意这里第二个参数forceUpdatetrue强制更新

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

Configuration updateOrientation(WindowContainer<?> freezeDisplayWindow, boolean forceUpdate) {
	if (!mDisplayReady) {
		return null;
	}

	Configuration config = null;
	if (updateOrientation(forceUpdate)) {
		// If we changed the orientation but mOrientationChangeComplete is already true,
		// we used seamless rotation, and we don't need to freeze the screen.
		if (freezeDisplayWindow != null && !mWmService.mRoot.mOrientationChangeComplete) {
			final ActivityRecord activity = freezeDisplayWindow.asActivityRecord();
			if (activity != null && activity.mayFreezeScreenLocked()) {
				activity.startFreezingScreen();
			}
		}
		config = new Configuration();
		computeScreenConfiguration(config);
	}

	return config;
}

如果需要更新Orientation创建new Configuration()调用computeScreenConfiguration

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

/**
 * Compute display configuration based on display properties and policy settings.
 * Do not call if mDisplayReady == false.
 */
void computeScreenConfiguration(Configuration config) {
	final DisplayInfo displayInfo = updateDisplayAndOrientation(config);
	final int dw = displayInfo.logicalWidth;
	final int dh = displayInfo.logicalHeight;
	mTmpRect.set(0, 0, dw, dh);
	config.windowConfiguration.setBounds(mTmpRect);
	config.windowConfiguration.setMaxBounds(mTmpRect);
	config.windowConfiguration.setWindowingMode(getWindowingMode());
	config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());

	computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation);

	config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
			| ((displayInfo.flags & Display.FLAG_ROUND) != 0
			? Configuration.SCREENLAYOUT_ROUND_YES
			: Configuration.SCREENLAYOUT_ROUND_NO);

	config.densityDpi = displayInfo.logicalDensityDpi;

	config.colorMode =
			((displayInfo.isHdr() && mWmService.hasHdrSupport())
					? Configuration.COLOR_MODE_HDR_YES
					: Configuration.COLOR_MODE_HDR_NO)
					| (displayInfo.isWideColorGamut() && mWmService.hasWideColorGamutSupport()
					? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
					: Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);

	// Update the configuration based on available input devices, lid switch,
	// and platform configuration.
	config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
	config.keyboard = Configuration.KEYBOARD_NOKEYS;
	config.navigation = Configuration.NAVIGATION_NONAV;

	int keyboardPresence = 0;
	int navigationPresence = 0;
	final InputDevice[] devices = mWmService.mInputManager.getInputDevices();
	final int len = devices != null ? devices.length : 0;
	for (int i = 0; i < len; i++) {
		InputDevice device = devices[i];
		// Ignore virtual input device.
		if (device.isVirtual()) {
			continue;
		}

		// Check if input device can dispatch events to current display.
		if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(), mDisplayId)) {
			continue;
		}

		final int sources = device.getSources();
		final int presenceFlag = device.isExternal()
				? WindowManagerPolicy.PRESENCE_EXTERNAL : WindowManagerPolicy.PRESENCE_INTERNAL;

		if (mWmService.mIsTouchDevice) {
			if ((sources & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) {
				config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
			}
		} else {
			config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
		}

		if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
			config.navigation = Configuration.NAVIGATION_TRACKBALL;
			navigationPresence |= presenceFlag;
		} else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
				&& config.navigation == Configuration.NAVIGATION_NONAV) {
			config.navigation = Configuration.NAVIGATION_DPAD;
			navigationPresence |= presenceFlag;
		}

		if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
			config.keyboard = Configuration.KEYBOARD_QWERTY;
			keyboardPresence |= presenceFlag;
		}
	}

	if (config.navigation == Configuration.NAVIGATION_NONAV && mWmService.mHasPermanentDpad) {
		config.navigation = Configuration.NAVIGATION_DPAD;
		navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
	}

	// Determine whether a hard keyboard is available and enabled.
	// TODO(multi-display): Should the hardware keyboard be tied to a display or to a device?
	boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
	if (hardKeyboardAvailable != mWmService.mHardKeyboardAvailable) {
		mWmService.mHardKeyboardAvailable = hardKeyboardAvailable;
		mWmService.mH.removeMessages(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
		mWmService.mH.sendEmptyMessage(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
	}

	mDisplayPolicy.updateConfigurationAndScreenSizeDependentBehaviors();

	// Let the policy update hidden states.
	config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
	config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
	config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
	mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
}

computeScreenAppConfiguration用来更新屏幕尺寸密度、rotation

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

/** Compute configuration related to application without changing current display. */
private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,
		int rotation) {
	final DisplayPolicy.DecorInsets.Info info =
			mDisplayPolicy.getDecorInsetsInfo(rotation, dw, dh);
	// AppBounds at the root level should mirror the app screen size.
	outConfig.windowConfiguration.setAppBounds(info.mNonDecorFrame);
	outConfig.windowConfiguration.setRotation(rotation);
	outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;

	final float density = mDisplayMetrics.density;
	outConfig.screenWidthDp = (int) (info.mConfigFrame.width() / density + 0.5f);
	outConfig.screenHeightDp = (int) (info.mConfigFrame.height() / density + 0.5f);
	outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale);
	outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);
	outConfig.screenLayout = computeScreenLayout(
			Configuration.resetScreenLayout(outConfig.screenLayout),
			outConfig.screenWidthDp, outConfig.screenHeightDp);

	final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
	outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dw, dh);
	outConfig.windowConfiguration.setDisplayRotation(rotation);
}

回到ensureVisibilityAndConfig 获取Configuration调用displayContent.updateDisplayOverrideConfigurationLocked

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

/**
 * Updates override configuration specific for the selected display. If no config is provided,
 * new one will be computed in WM based on current display info.
 */
boolean updateDisplayOverrideConfigurationLocked(Configuration values,
		ActivityRecord starting, boolean deferResume,
		ActivityTaskManagerService.UpdateConfigurationResult result) {

	int changes = 0;
	boolean kept = true;

	mAtmService.deferWindowLayout();
	try {
		if (values != null) {
			if (mDisplayId == DEFAULT_DISPLAY) {
				// Override configuration of the default display duplicates global config, so
				// we're calling global config update instead for default display. It will also
				// apply the correct override config.
				changes = mAtmService.updateGlobalConfigurationLocked(values,
						false /* initLocale */, false /* persistent */,
						UserHandle.USER_NULL /* userId */);
			} else {
				changes = performDisplayOverrideConfigUpdate(values);
			}
		}

		if (!deferResume) {
			kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
		}
	} finally {
		mAtmService.continueWindowLayout();
	}

	if (result != null) {
		result.changes = changes;
		result.activityRelaunched = !kept;
	}
	return kept;
}

单个屏幕场景mDisplayId == DEFAULT_DISPLAY调用mAtmService.updateGlobalConfigurationLocked

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

/** Update default (global) configuration and notify listeners about changes. */
int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
		boolean persistent, int userId) {

	mTempConfig.setTo(getGlobalConfiguration());
	final int changes = mTempConfig.updateFrom(values);
	if (changes == 0) {
		return 0;
	}

	...

	// Note: certain tests currently run as platform_app which is not allowed
	// to set debug system properties. To ensure that system properties are set
	// only when allowed, we check the current UID.
	if (Process.myUid() == Process.SYSTEM_UID) {
		if (values.mcc != 0) {
			SystemProperties.set("debug.tracing.mcc", Integer.toString(values.mcc));
		}
		if (values.mnc != 0) {
			SystemProperties.set("debug.tracing.mnc", Integer.toString(values.mnc));
		}
	}

	if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
		final LocaleList locales = values.getLocales();
		int bestLocaleIndex = 0;
		if (locales.size() > 1) {
			if (mSupportedSystemLocales == null) {
				mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
			}
			bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
		}
		SystemProperties.set("persist.sys.locale",
				locales.get(bestLocaleIndex).toLanguageTag());
		LocaleList.setDefault(locales, bestLocaleIndex);
	}

	mTempConfig.seq = increaseConfigurationSeqLocked();

	Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
	// TODO(multi-display): Update UsageEvents#Event to include displayId.
	mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());

	// TODO: If our config changes, should we auto dismiss any currently showing dialogs?
	updateShouldShowDialogsLocked(mTempConfig);

	AttributeCache ac = AttributeCache.instance();
	if (ac != null) {
		ac.updateConfiguration(mTempConfig);
	}

	// Make sure all resources in our process are updated right now, so that anyone who is going
	// to retrieve resource values after we return will be sure to get the new ones. This is
	// especially important during boot, where the first config change needs to guarantee all
	// resources have that config before following boot code is executed.
	mSystemThread.applyConfigurationToResources(mTempConfig);

	if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
		final Message msg = PooledLambda.obtainMessage(
				ActivityTaskManagerService::sendPutConfigurationForUserMsg,
				this, userId, new Configuration(mTempConfig));
		mH.sendMessage(msg);
	}

	SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap();
	for (int i = pidMap.size() - 1; i >= 0; i--) {
		final int pid = pidMap.keyAt(i);
		final WindowProcessController app = pidMap.get(pid);
		ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new "
				+ "config %s", app.mName, mTempConfig);
		app.onConfigurationChanged(mTempConfig);
	}

	final Message msg = PooledLambda.obtainMessage(
			ActivityManagerInternal::broadcastGlobalConfigurationChanged,
			mAmInternal, changes, initLocale);
	mH.sendMessage(msg);

	Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RootConfigChange");
	// Update stored global config and notify everyone about the change.
	mRootWindowContainer.onConfigurationChanged(mTempConfig);
	Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);

	Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
	return changes;
}

调用所用进程对应WindowProcessControlleronConfigurationChanged方法 这个方法最终会通知到客户端应用的所有Activity、Service等。随后通过broadcastGlobalConfigurationChanged发送ACTION_CONFIGURATION_CHANGED广播最后调用mRootWindowContainer.onConfigurationChanged(mTempConfig);这个通知服务端RootWindowContainer及其所有子窗口进行configuration更新

客户端传递路线参考序列图最终调用ConfigurationControllerhandleConfigurationChanged

platform/frameworks/base/core/java/android/app/ConfigurationController.java

/**
 * Update the configuration to latest.
 * @param config The new configuration.
 * @param compat The new compatibility information.
 */
void handleConfigurationChanged(@Nullable Configuration config,
		@Nullable CompatibilityInfo compat) {
	int configDiff;
	boolean equivalent;

	// Get theme outside of synchronization to avoid nested lock.
	final Resources.Theme systemTheme = mActivityThread.getSystemContext().getTheme();
	final ContextImpl systemUiContext = mActivityThread.getSystemUiContextNoCreate();
	final Resources.Theme systemUiTheme =
			systemUiContext != null ? systemUiContext.getTheme() : null;
	synchronized (mResourcesManager) {
		if (mPendingConfiguration != null) {
			if (!mPendingConfiguration.isOtherSeqNewer(config)) {
				config = mPendingConfiguration;
				updateDefaultDensity(config.densityDpi);
			}
			mPendingConfiguration = null;
		}

		if (config == null) {
			return;
		}

		// This flag tracks whether the new configuration is fundamentally equivalent to the
		// existing configuration. This is necessary to determine whether non-activity callbacks
		// should receive notice when the only changes are related to non-public fields.
		// We do not gate calling {@link #performActivityConfigurationChanged} based on this
		// flag as that method uses the same check on the activity config override as well.
		equivalent = mConfiguration != null && (0 == mConfiguration.diffPublicOnly(config));

		if (DEBUG_CONFIGURATION) {
			Slog.v(TAG, "Handle configuration changed: " + config);
		}

		final Application app = mActivityThread.getApplication();
		final Resources appResources = app.getResources();
		mResourcesManager.applyConfigurationToResources(config, compat);
		updateLocaleListFromAppContext(app.getApplicationContext());

		if (mConfiguration == null) {
			mConfiguration = new Configuration();
		}
		if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
			return;
		}

		configDiff = mConfiguration.updateFrom(config);
		config = applyCompatConfiguration();
		HardwareRenderer.sendDeviceConfigurationForDebugging(config);

		if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
			systemTheme.rebase();
		}

		if (systemUiTheme != null
				&& (systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
			systemUiTheme.rebase();
		}
	}

	final ArrayList<ComponentCallbacks2> callbacks =
			mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);

	freeTextLayoutCachesIfNeeded(configDiff);

	if (callbacks != null) {
		final int size = callbacks.size();
		for (int i = 0; i < size; i++) {
			ComponentCallbacks2 cb = callbacks.get(i);
			if (!equivalent) {
				performConfigurationChanged(cb, config);
			}
		}
	}
}

mResourcesManager.applyConfigurationToResources(config, compat);config更新resourceManager

configDiff = mConfiguration.updateFrom(config);config = applyCompatConfiguration();更新本地config让后通过performConfigurationChanged传导所有AcivityService

无论WindowProcessControlleronConfigurationChanged还是mRootWindowContainer.onConfigurationChanged 首先执行基类ConfigurationContaineronConfigurationChanged

platform/frameworks/base/services/core/java/com/android/server/wm/ConfigurationContainer.java

/**
 * Notify that parent config changed and we need to update full configuration.
 * @see #mFullConfiguration
 */
public void onConfigurationChanged(Configuration newParentConfig) {
	mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
	resolveOverrideConfiguration(newParentConfig);
	mFullConfiguration.setTo(newParentConfig);
	// Do not inherit always-on-top property from parent, otherwise the always-on-top
	// property is propagated to all children. In that case, newly added child is
	// always being positioned at bottom (behind the always-on-top siblings).
	mFullConfiguration.windowConfiguration.unsetAlwaysOnTop();
	mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
	onMergedOverrideConfigurationChanged();
	if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
		// This depends on the assumption that change-listeners don't do
		// their own override resolution. This way, dependent hierarchies
		// can stay properly synced-up with a primary hierarchy's constraints.
		// Since the hierarchies will be merged, this whole thing will go away
		// before the assumption will be broken.
		// Inform listeners of the change.
		for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
			mChangeListeners.get(i).onRequestedOverrideConfigurationChanged(
					mResolvedOverrideConfiguration);
		}
	}
	for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
		mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
				mMergedOverrideConfiguration);
	}
	for (int i = getChildCount() - 1; i >= 0; --i) {
		dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
	}
}

这个方法Configuration相关一些重要属性进行更新详述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[NOIP1998 提高组] 拼数

[NOIP1998 提高组] 拼数 题目描述 设有 n n n 个正整数 a 1 … a n a_1 \dots a_n a1​…an​&#xff0c;将它们联接成一排&#xff0c;相邻数字首尾相接&#xff0c;组成一个最大的整数。 输入格式 第一行有一个整数&#xff0c;表示数字个数 n n n。 第二行有 n n …

【Vue3遇见的问题】创建vue3的项目使用vscode打开后项目的app.vue里面存在爆红

出现的问题 直接上上问题:问题的图片如下: 解决方法 解决效果 补充 因为vetur的插件禁用了 所以需要一个新插件来 这里发现的官网推荐的插件 也就是volar 他两是一样的

各位老板,你需要的工厂数字孪生可视化库在这

各位老板是不是很喜欢下面这种有逼格的大屏,下面介绍一下怎么实现的,保证有所收获。 Cesium是一个开源的WebGL JavaScript库&#xff0c;用于创建高性能的三维地球、地图和虚拟环境。它支持在浏览器中实现高质量的地球模拟&#xff0c;同时提供了丰富的功能特点&#xff0c;使得…

【Linux进程的状态】

目录 看Linux源码中的说法 如何查看进程状态&#xff1f; 各个状态的关系 僵尸进程 举个栗子 现象 僵尸进程的危害 孤儿进程 举个栗子 现象 进程的优先级 基本概念 为什么要有进程优先级&#xff1f; 查看系统进程 进程的大致属性 进程优先级vs进程的权限 Linu…

AI基础知识(4)--贝叶斯分类器

1.什么是贝叶斯判定准则&#xff08;Bayes decision rule&#xff09;&#xff1f;什么是贝叶斯最优分类器&#xff08;Bayes optimal classifier&#xff09;&#xff1f; 贝叶斯判定准则&#xff1a;为最小化总体风险&#xff0c;只需在每个样本上选择那个能使条件风险最小的…

用 Open-Sora 高效创作视频,让创意触手可及

近年来&#xff0c;视频内容以爆炸式增长席卷了我们的生活。从短视频平台到直播带货&#xff0c;视频正成为人们获取信息和娱乐的主要方式。然而&#xff0c;传统视频制作流程往往耗时费力&#xff0c;对于普通用户来说门槛较高。 为了降低视频创作门槛&#xff0c;让更多人享…

Git的 .gitignore文件及标签使用

Git的 .gitignore文件及标签使用 什么是.gitignoregit check-ignore -v 文件名 查看.gitignore里面什么内容忽略了该文件 git add -f [filename] 强制添加把指定文件排除在 .gitignore 规则外的写法给命令配置别名标签创建标签git tag [name] 创建标签git tag 列出所有标签git …

RESNET的复现pytorch版本

RESNET的复现pytorch版本 使用的数据为Object_102_CaDataset&#xff0c;可以在网上下载&#xff0c;也可以在评论区问。 RESNET模型的亮点 1.提出了残差模块。 2.使用Batch Normalization加速训练 3.残差网络&#xff1a;易于收敛&#xff0c;很好的解决了退化问题&#…

真实数据!一张切片实现101种蛋白的超多重空间单细胞原位成像

头颈鳞状细胞癌 (HNSCC) 是第七大常见癌症。免疫检查点抑制剂 (ICIs) 在治疗复发/转移病例方面显示出良好前景&#xff0c;约30%的患者可获得持久获益。但是目前反映HNSCC肿瘤微环境 (TME) 特征的生物标志物有限&#xff0c;需要更深入的组织表征分析。因此&#xff0c;需要新的…

linux查看cpu/内存/磁盘利用率

1、cpu 命令&#xff1a; top 2、内存 命令&#xff1a; free -h 3、磁盘 命令&#xff1a; df -h

《操作系统真相还原》读书笔记九:用c编写内核

用c语言先编写一个死循环 main.c int main(void) {while(1);return 0; }编译该文件 gcc -c -o main.o main.c-- Ttext参数表示起始虚拟地址为0xc0001500 -e参数表示程序入口地址 ld main.o -Ttext 0xc0001500 -e main -o kernel.bin-- 将kernel.bin写入第9个扇区 dd if/ho…

十九、网络编程

目录 一、什么是网络编程二、网络编程三要素2.1 IP2.2 InetAddress的使用2.3 端口号2.4 协议 三、UDP通信程序3.1 发送数据3.2 接收数据3.3 练习 四、UDP的三种通信方式五、TCP的通信程序六、三次握手和四次挥手七、练习7.1 TCP通信练习1——多发多收7.2 TCP通信练习2——接收和…

Cookie使用

文章目录 一、Cookie基本使用1、发送Cookie2、获取Cookie 二、Cookie原理三、Cookie使用细节 一、Cookie基本使用 1、发送Cookie package com.itheima.web.cookie;import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import java.io.I…

82.删除排序链表中的重复元素II

给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,3,4,4,5] 输出&#xff1a;[1,2,5]示例 2&#xff1a; 输入&#xff1a;head [1,1,1,2…

【OJ比赛日历】快周末了,不来一场比赛吗? #03.23-03.29 #16场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2024-03-23&#xff08;周六&#xff09; #7场比赛2024-03-24…

高级数据结构 <AVL树>

本文已收录至《数据结构(C/C语言)》专栏&#xff01; 作者&#xff1a;ARMCSKGT 目录 前言正文AVL树的性质AVL树的定义AVL树的插入函数左单旋右单旋右左双旋左右双旋 检验AVL树的合法性关于AVL树 最后 前言 前面我们学习了二叉树&#xff0c;普通的二叉树没有任何特殊性质&…

C语言易错知识点:二级指针、数组指针、函数指针

指针在C语言中非常关键&#xff0c;除开一些常见的指针用法&#xff0c;还有一些可能会比较生疏&#xff0c;但有时却也必不可少&#xff0c;本文章整理了一些易错知识点&#xff0c;希望能有所帮助&#xff01; 1.二级指针&#xff1a; parr是一个指针数组&#xff0c;其中每…

GEE遥感云大数据林业应用典型案例及GPT模型应用

近年来遥感技术得到了突飞猛进的发展&#xff0c;航天、航空、临近空间等多遥感平台不断增加&#xff0c;数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量猛增&#xff0c;遥感数据已经越来越具有大数据特征。遥感大数据的出现为相关研究提供了前所未有的机遇&#xf…

数据结构:初识树和二叉树

目前主流的方式是左孩子右兄弟表示法 我们的文件系统就是一个树 以上就是树的概念&#xff0c;我们今天还要来学习一种从树演变的重要的结构&#xff1a;二叉树 顾名思义二叉树就是一个结点最多有两个子树。 其中我们还要了解满二叉树和完全二叉树的概念 注意我们的完全二叉…

【一起学Rust | 基础篇】rust线程与并发

文章目录 前言一、创建线程二、mpsc多生产者单消费者模型1.创建一个简单的模型2.分批发送数据3. 使用clone来产生多个生产者 三、共享状态&#xff1a;互斥锁1. 创建一个简单的锁2. 使用互斥锁解决引用问题 前言 并发编程&#xff08;Concurrent programming&#xff09;&#…
最新文章