android PKMS服务

前面我们介绍过Android AMS服务,今天我们看一下PKMS(PackageManagerService)服务,PKMS也是android系统中核心服务之一,负责应用程序的安装,卸载,信息查询等工作。

PKMS的启动

首先启动创建PKMS:

private void startBootstrapServices() { ... // code 1 启动Installer // 我们需要在初始化其他服务之前完成此任务。 Installer installer = mSystemServiceManager.startService(Installer.class);...mActivityManagerService.setInstaller(installer);... // code 2 获取设备是否加密(手机设置密码),如果加密了,则只解析"core"应用,mOnlyCore = true,后面会频繁使用该变量进行条件判断 (android 14已去除)String cryptState = VoldProperties.decrypt().orElse("");if (ENCRYPTING_STATE.equals(cryptState)) {Slog.w(TAG, "Detected encryption in progress - only parsing core apps");mOnlyCore = true;} else if (ENCRYPTED_STATE.equals(cryptState)) {Slog.w(TAG, "Device encrypted - only parsing core apps");mOnlyCore = true;}// code 3 调用main方法初始化PackageManagerService mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);// PKMS是否是第一次启动mFirstBoot = mPackageManagerService.isFirstBoot(); mPackageManager = mSystemContext.getPackageManager();// code 4 如果设备没有加密,执行OtaDexoptService.main。 if (!mOnlyCore) {boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt", false); OtaDexoptService.main(mSystemContext, mPackageManagerService); }... 
}private void startOtherServices() {...//检查system app是否需要更新,需要则更新mPackageManagerService.updatePackagesIfNeeded();...//检查system app是否需要fs裁剪mPackageManagerService.performFstrimIfNeeded();...//最后system Ready通知其他服务ready设置相关信息mPackageManagerService.systemReady();...
}

在startBootstrpServices中,会做以下几件事情:

  1. 启动Installer服务
  2. 获取设备是否加密(手机设置密码),如果设备加密了,则只解析"core"应用(在android14中被删除)
  3. 调用PKMS main方法初始化PackageManagerService,其中调用PackageManagerService()构造函数创建了PKMS对象
  4. 如果设备没有加密,执行OtaDexoptService.main。

我们继续看PackageManagerService.main:

public static PackageManagerService main(Context context,Installer installer, @NonNull DomainVerificationService domainVerificationService,boolean factoryTest) {// code 1 检查Package编译相关系统属性PackageManagerServiceCompilerMapping.checkProperties();...// code 2 调用PackageManagerService构造方法PackageManagerService m = new PackageManagerService(injector, factoryTest,PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG,Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL);...m.installAllowlistedSystemPackages();IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();// code 3 往ServiceManager中注册”package”和”package_native”ServiceManager.addService("package", iPackageManager);final PackageManagerNative pmn = new PackageManagerNative(m);ServiceManager.addService("package_native", pmn);return m;
}

在构造方法中会处理很多的逻辑:

初始化Settings

扫描各个系统目录下APP相关信息

public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {PackageManager.disableApplicationInfoCache();PackageManager.disablePackageInfoCache();...//code 1 setting保存系统中安装APP包名,权限,四大组件等相关信息的存储mSettings = injector.getSettings();...// CHECKSTYLE:OFF IndentationChecksynchronized (mInstallLock) {// writersynchronized (mLock) {...// Collect vendor/product/system_ext overlay packages. (Do this before scanning// any apps.)// For security and version matching reason, only consider overlay packages if they// reside in the right directory.for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {final ScanPartition partition = mDirsToScanAsSystem.get(i);if (partition.getOverlayFolder() == null) {continue;}scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,systemScanFlags | partition.scanFlag, 0,packageParser, executorService);}scanDirTracedLI(frameworkDir, systemParseFlags,systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,packageParser, executorService);if (!mPackages.containsKey("android")) {throw new IllegalStateException("Failed to load frameworks package; check log for warnings");}for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {final ScanPartition partition = mDirsToScanAsSystem.get(i);if (partition.getPrivAppFolder() != null) {scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,packageParser, executorService);}scanDirTracedLI(partition.getAppFolder(), systemParseFlags,systemScanFlags | partition.scanFlag, 0,packageParser, executorService);}...if (!mOnlyCore) {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,SystemClock.uptimeMillis());scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,packageParser, executorService);}packageParser.close();...}}
}

继续执行scanDirTraceLI

private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,long currentTime, PackageParser2 packageParser, ExecutorService executorService) {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");try {scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}
}

继续执行scanDirLI

private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,PackageParser2 packageParser, ExecutorService executorService) {final File[] files = scanDir.listFiles();if (ArrayUtils.isEmpty(files)) {Log.d(TAG, "No files in app dir " + scanDir);return;}if (DEBUG_PACKAGE_SCANNING) {Log.d(TAG, "Scanning app dir " + scanDir + " scanFlags=" + scanFlags+ " flags=0x" + Integer.toHexString(parseFlags));}ParallelPackageParser parallelPackageParser =new ParallelPackageParser(packageParser, executorService);// Submit files for parsing in parallelint fileCount = 0;for (File file : files) {final boolean isPackage = (isApkFile(file) || file.isDirectory())&& !PackageInstallerService.isStageName(file.getName());if (!isPackage) {// Ignore entries which are not packagescontinue;}parallelPackageParser.submit(file, parseFlags);fileCount++;}// Process results one by onefor (; fileCount > 0; fileCount--) {ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();Throwable throwable = parseResult.throwable;int errorCode = PackageManager.INSTALL_SUCCEEDED;if (throwable == null) {// TODO(toddke): move lower in the scan chain// Static shared libraries have synthetic package namesif (parseResult.parsedPackage.isStaticSharedLibrary()) {renameStaticSharedLibraryPackage(parseResult.parsedPackage);}try {addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,currentTime, null);} catch (PackageManagerException e) {errorCode = e.error;Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());}} else if (throwable instanceof PackageParserException) {PackageParserException e = (PackageParserException)throwable;errorCode = e.error;Slog.w(TAG, "Failed to parse " + parseResult.scanFile + ": " + e.getMessage());} else {throw new IllegalStateException("Unexpected exception occurred while parsing "+ parseResult.scanFile, throwable);}if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath());}// Delete invalid userdata appsif ((scanFlags & SCAN_AS_SYSTEM) == 0&& errorCode != PackageManager.INSTALL_SUCCEEDED) {logCriticalInfo(Log.WARN,"Deleting invalid package at " + parseResult.scanFile);removeCodePathLI(parseResult.scanFile);}}
}

扫描路径的顺序

/vendor/overlay
/product/overlay
/product_services/overlay
/odm/overlay
/oem/overlay
/system/framework
/system/priv-app
/system/app
/vendor/priv-app
/vendor/app
/odm/priv-app
/odm/app
/oem/app
/product/priv-app
/product/app
/product_services/priv-app
/product_services/app

PKMS安装APK

Binder.execTransact->
Binder.execTransactInternal->
PackageManagerService.onTransact->
IPackageManager$Stub.onTransact->
Binder.onTransact->
Binder.shellCommand->
PackageManagerService.onShellCommand->
ShellCommand.exec->
PackageManagerShellCommand.onCommand->
PackageManagerShellCommand.runInstall->
PackageManagerShellCommand.doRunInstall->
PackageManagerShellCommand.doCommitSession->
PackageInstaller$Session.commit->
PackageInstallerSession.commit->
PackageInstallerSession.dispatchStreamValidateAndCommit->(mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT))
PackageInstallerSession.handleStreamValidateAndCommit->mHandler.obtainMessage(MSG_INSTALL)
PackageInstallerSession.handleInstall->
PackageInstallerSession.installNonStagedLocked->
PackageManagerService.installStage->mHandler.obtainMessage(INIT_COPY)
PackageManagerService$HandlerParams.startCopy

我们通过上面的代码可以看到,最终会执行到PackageManagerService$HandlerParams.startCopy:

final void startCopy() {if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);handleStartCopy();handleReturnCode();
}

继续执行handleReturnCode,最终会调用ParsingPackageUtils.parsePackage:

PackageManagerService$HandlerParams.startCopy->
PackageManagerService.handleReturnCode->
PackageManagerService.processPendingInstall->
PackageManagerService.processInstallRequestsAsync->
PackageManagerService.installPackagesTracedLI->
PackageManagerService.installPackagesLI->
PackageManagerService.scanPackageTracedLI->
PackageManagerService.scanPackageLI->
PackageParser2.parsePackage->
ParsingPackageUtils.parsePackage

我们看一下parsePackage的逻辑: 

​
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,int flags)throws PackageParserException {if (packageFile.isDirectory()) {return parseClusterPackage(input, packageFile, flags);} else {return parseMonolithicPackage(input, packageFile, flags);}
}​

上面代码中,如果参数是packageFile 是一个目录,就会执行parseClusterPackage(),否则执行 parseMonolithicPackage() 来处理。对于关联的多个apk 会放到一个目录下,来查看parseClusterPackage():

private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,int flags) {//获取应用目录的PackageLite 对象,这个对象中分开保存了目录下的核心应用名称以及其他非核心应用的名称ParseResult<PackageParser.PackageLite> liteResult =ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);if (liteResult.isError()) {return input.error(liteResult);}final PackageParser.PackageLite lite = liteResult.getResult();if (mOnlyCoreApps && !lite.coreApp) {return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,"Not a coreApp: " + packageDir);}// Build the split dependency tree.SparseArray<int[]> splitDependencies = null;final SplitAssetLoader assetLoader;if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {try {splitDependencies =SplitAssetDependencyLoader.createDependenciesFromPackage(lite);assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);} catch (SplitAssetDependencyLoader.IllegalDependencyException e) {return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());}} else {assetLoader = new DefaultSplitAssetLoader(lite, flags);}try {//需要AssetManager 对象final AssetManager assets = assetLoader.getBaseAssetManager();final File baseApk = new File(lite.baseCodePath);ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,lite.codePath, assets, flags);if (result.isError()) {return input.error(result);}//对于apk 进行分析,得到Package 对象ParsingPackage pkg = result.getResult();if (!ArrayUtils.isEmpty(lite.splitNames)) {pkg.asSplit(lite.splitNames,lite.splitCodePaths,lite.splitRevisionCodes,splitDependencies);final int num = lite.splitNames.length;for (int i = 0; i < num; i++) {final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);parseSplitApk(input, pkg, i, splitAssets, flags);}}pkg.setUse32BitAbi(lite.use32bitAbi);return input.success(pkg);} catch (PackageParserException e) {return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,"Failed to load assets: " + lite.baseCodePath, e);} finally {IoUtils.closeQuietly(assetLoader);}
}

parseClusterPackage() 方法中先执行parseClusterPackageLite() 方法对目录下的apk 文件进行初步分析,主要是区分出核心应用和非核心应用。核心应用只有一个,非核心应用可以没有或者多个,非核心应用的作用是用来保存资源和代码。

接下来调用parseBaseApk() 方法对核心应用进行分析,并生成Package 对象,对非核心的应用调用 parseSpliteApk() 方法来分析,分析的结果会放到前面的 Package 对象中。 parseBaseApk() 方法实际上是主要是分析AndroidManifest.xml 文件:

private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,String codePath, AssetManager assets, int flags) {final String apkPath = apkFile.getAbsolutePath();...final int cookie = assets.findCookieForPath(apkPath);...try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,PackageParser.ANDROID_MANIFEST_FILENAME)) { // code 1final Resources res = new Resources(assets, mDisplayMetrics, null);ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,parser, flags);...final ParsingPackage pkg = result.getResult();...pkg.setVolumeUuid(volumeUuid);...return input.success(pkg);} catch(Exception e){return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,"Failed to read manifest from "+apkPath,e);}
}

在code1处主要分析XmlResourceParser对象,也就是获得一个 XML 资源解析对象,该对象解析的是 APK 中的 AndroidManifest.xml 文件

parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

查看PackageParser文件可知,这个地方的ANDROID_MANIFEST_FILENAME 为

 public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";

继续执行parseBaseApk:

ParsingPackageUtils.parsePackage->
ParsingPackageUtils.parseClusterPackage->
ParsingPackageUtils.parseBaseApk->
ParsingPackageUtils.parseBaseApk->
ParsingPackageUtils.parseBaseApkTags->
ParsingPackageUtils.parseBaseApplication->
ParsedActivityUtils.parseActivityOrReceiver

最终会执行到ParsingPackageUtils.parseBaseApplication:

private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)throws XmlPullParserException, IOException {...switch (tagName) {case "activity":isActivity = true;// fall-throughcase "receiver":ParseResult<ParsedActivity> activityResult =ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,res, parser, flags, PackageParser.sUseRoundIcon, input);if (activityResult.isSuccess()) {ParsedActivity activity = activityResult.getResult();if (isActivity) {hasActivityOrder |= (activity.getOrder() != 0);pkg.addActivity(activity);} else {hasReceiverOrder |= (activity.getOrder() != 0);pkg.addReceiver(activity);}}result = activityResult;break;case "service":ParseResult<ParsedService> serviceResult =ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,flags, PackageParser.sUseRoundIcon, input);if (serviceResult.isSuccess()) {ParsedService service = serviceResult.getResult();hasServiceOrder |= (service.getOrder() != 0);pkg.addService(service);}result = serviceResult;break;case "provider":ParseResult<ParsedProvider> providerResult =ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,flags, PackageParser.sUseRoundIcon, input);if (providerResult.isSuccess()) {pkg.addProvider(providerResult.getResult());}result = providerResult;break;case "activity-alias":activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,parser, PackageParser.sUseRoundIcon, input);if (activityResult.isSuccess()) {ParsedActivity activity = activityResult.getResult();hasActivityOrder |= (activity.getOrder() != 0);pkg.addActivity(activity);}result = activityResult;break;default:result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);break;}...
}

 在这个地方就完成了activity,service,receiver等四大组件的注册。

总结

  • 清单文件的解析过程,一般是由PKMS来完成,触发PKMS的执行的分为两个部分:
    1. 在系统启动的过程中,会启动 SystemServer 进程,而SystemServer进程会启动各种服务,这些服务包括PKMS,在启动PKMS的时候就会扫码apk 安装路径下面的apk,然后解析AndroidManifest文件,并做持久化存储;
    2. app 安装的过程中,也会触发PKMS对 apk进行检测,调用类似的流程解析 AndroidManifest文件。Receiver的解析,都只是整个AndroidManifest解析过 程中的一个环节。
  • android系统启动之后会解析系统特定目录下的apk文件,并执行解析;
  • 解析Manifest流程:Zygote进程 –> SystemServer进程 –> PackgeManagerService服务 –>  scanPackageLI方法 –> PackageParser2.parserPackage方法;
  • 解析完成Manifest之后会将apk的Manifest信息保存在Settings对象中并持久化,删除软件时,信息会被删除,新安装的apk会重复调用scanPackageLI。

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

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

相关文章

给网站添加安全措施 -- http -> https

快速上手 1、准备&#xff1a;腾讯云http域名、SSL证书、Nginx服务器。 2、在腾讯云平台内-我的域名&#xff0c;找到指定域名后添加解析映射IP。 3、腾讯云上搜索SSL证书&#xff0c;然后申请免费证书&#xff08;1个工作日内通过&#xff09;。 4、证书通过后&#xff0c;下…

深度学习算法概念介绍

前言 深度学习算法是一类基于人工神经网络的机器学习方法&#xff0c;其核心思想是通过多层次的非线性变换&#xff0c;从数据中学习表示层次特征&#xff0c;从而实现对复杂模式的建模和学习。深度学习算法在图像识别、语音识别、自然语言处理等领域取得了巨大的成功&#xf…

【STM32 HAL库SPI/QSPI协议学习,基于外部Flash读取。】

1、SPI协议 简介 SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface)&#xff0c;即串行外围设备接口&#xff0c;是 一种高速全双工的通信总线。它被广泛地使用在 ADC、LCD 等设备与 MCU 间&#xff0c;要求通讯速率 较高的场合。 SPI 物理层 SPI 通讯…

Nginx(Docker 安装的nginx)配置域名SSL证书

1.首先确保Linux环境上已经安装了docker&#xff08;可参考Linux安装Docker-CSDN博客&#xff09; 2.通过docker 安装nginx&#xff08;可参考Linux 环境安装Nginx—源码和Dokcer两种安装方式-CSDN博客&#xff09; 3.安装SSL证书 3.1 在宿主机中创建证书目录并上传证书&…

【数据结构与算法篇】动态顺序表及相关OJ算法题

【数据结构与算法篇】动态顺序表及相关OJ算法题 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;数据结构与算法&#x1f345; 目录 【数据结构与算法篇】动态顺序表及相关OJ算法题 1. 动态顺序表的实现 1.1 SeqList.h 头文件声明 1.…

基于LSB(最低有效位)的图像水印算法,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

php将网页用wkhtmltoimage内容生成为图片

php架构ThinkPHP6 1. 安装 knp-snappy架构 composer require knplabs/knp-snappy use Knp\Snappy\Image; use Illuminate\Support\Facades\Storage;// 生成图片 /user/local/bin/wkhtmltoimage为你的wkhtmltoimage的位置。 $snappy new Image(/usr/local/bin/wkhtmltoimage…

智慧城市数字孪生,综合治理一屏统览

现代城市作为一个复杂系统&#xff0c;牵一发而动全身&#xff0c;城市化进程中产生新的矛盾和社会问题都会影响整个城市系统的正常运转。智慧城市是应对这些问题的策略之一。城市工作要树立系统思维&#xff0c;从构成城市诸多要素、结构、功能等方面入手&#xff0c;系统推进…

【鸿蒙HarmonyOS开发笔记】通用型工具封装之关系型数据库操作类的封装

概述 开发中难免遇到操作关系型数据库的场景&#xff0c;但是原生的relationalStore使用起来略显繁琐&#xff0c;此文封装一个通用的关系型数据库增删改查的工具类&#xff0c;只需要少量修改配置即可使用&#xff0c;大幅简化我们的开发成本&#xff0c;提高开发效率 完整代…

算法学习——LeetCode力扣单调栈篇

算法学习——LeetCode力扣单调栈篇 739. 每日温度 739. 每日温度 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个…

vue图片压缩

1.使用插件 1.image-conversion 1.1安装 npm i image-conversion --save 1.2使用 //引用 import * as imageConversion from image-conversion//使用(主要方法)//压缩到指定大小 beforeAvatarUpload(file) {const isJpgOrPng file.type image/jpeg || file.type image/png…

关系型数据库mysql(7)sql高级语句①

目录 一.MySQL常用查询 1.按关键字&#xff08;字段&#xff09;进行升降排序 按分数排序 &#xff08;默认为升序&#xff09; 按分数升序显示 按分数降序显示 根据条件进行排序&#xff08;加上where&#xff09; 根据多个字段进行排序 ​编辑 2.用或&#xff08;or&…

Rust语言中Option和Result两种类型的使用

异同之处 不同之处主要在于用途不同 都可以用于Error handle的&#xff0c;都是枚举类型,都可以作为function的返回类型&#xff0c; 二者源码如下 pub enum Option<T> {None,Some(T), }pub enum Result<T, E> {Ok(T),Err(E), }为什么这么设计 有场景才有设计的…

ESP8266 控制 LED 亮灭

一、引脚对应 二、按键控制 LED 亮灭 2.1样例1 #include <ESP8266WiFi.h>const int ledPin D2; // LED 连接到 D2 引脚 const int keyPin D4; // 按键连接到 D4 引脚volatile bool flag false; // 记录 LED 状态的标志// 外部中断处理函数 ICACHE_RAM_ATTR void han…

重新温习广软puthon爬虫技术。

下面是我不断试错的一个过程&#xff0c;好多知识点全忘记了&#xff0c;只能不断调实例&#xff0c;不断优化&#xff0c;重构&#xff0c;实现自己的需求。下面是我的运行截图。还是导包的问题。 个人感觉关键的还是这几部&#xff0c;被划了下划线的&#xff0c;存在问题&a…

【智能算法】猎人猎物算法(HPO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2022年&#xff0c;Naruei等人受到自然界动物猎食过程启发&#xff0c;提出了猎人猎物算法&#xff08;Hunter-Prey Optimization&#xff0c; HPO&#xff09;。 2.算法原理 2.1算法思想 HPO模拟…

输出1到10的阶乘--C语言

#include<stdio.h> int fac(int n){if(n<1){return 1;}elsereturn fac(n-1)*n; } int main(){int i, result;for(i1;i<10;i){resultfac(i);printf("%d!%d\n",i,result);}} 输出结果&#xff1a;

Java并发编程基础_Thread类

线程 Thread.class 1. 线程的六种状态 NEW 尚未启动的线程处于此状态。RUNNABLE 在Java虚拟机中执行的线程处于此状态。BLOCKED 被阻塞等待监视器锁定的线程处于此状态。WAITING 正在等待另一个线程执行特定动作的线程处于此状态。TIMED_WAITING 正在等待另一个线程执行动作达到…

C++中浅拷贝和深拷贝对象复制概念

1.浅拷贝&#xff08;Shallow Copy&#xff09;&#xff1a; 浅拷贝是指在对象复制时&#xff0c;只是复制对象的值&#xff0c;而不会复制对象指向的资源。这意味着对象和其副本会指向同一块内存空间&#xff0c;当一个对象改变时&#xff0c;另一个对象也会受到影响。 #inclu…

中国电子学会(CEIT)2021年09月真题C语言软件编程等级考试四级(含详细解析答案)

中国电子学会&#xff08;CEIT&#xff09;考评中心历届真题&#xff08;含解析答案&#xff09; C语言软件编程等级考试四级 2021年09月 编程题四道 总分:100分一、吃奶酪&#xff08;25分&#xff09; Jerry准备偷吃Tom的奶酪。所有的奶酪排成了一条直线&#xff0c;…