当前位置: 首页 > news >正文

Android应用安装过程

Android 系统源码源码-应用安装过程

Android 中应用安装的过程就是解析 AndroidManifest.xml 的过程,系统可以从 Manifest 中得到应用程序的相关信息,比如 Activity、Service、Broadcast Receiver 和 ContentProvider 等。这些工作都是由 PackageManageService 负责的,也就是所谓的 PMS. 它跟 AMS 一样都是一种远程的服务,并且都是在系统启动 SystemServer 的时候启动的。下面我们通过源代码来分析下这个过程。

1、启动 PMS 的过程

系统在启动 SystemServer 的过程会启动 PMS,系统的启动过程可以参考下面这篇文章学习,

Android 系统源码-1:Android 系统启动流程源码分析

在启动 SystemServer 的时候会调用 startBootstrapServices() 方法启动引导服务。PMS 就是在这个方法中启动的,

    private void startBootstrapServices() {// ...mPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);mFirstBoot = mPackageManagerService.isFirstBoot();mPackageManager = mSystemContext.getPackageManager();// ...}

可以看出,系统是通过调用 PMS 的 main 方法来将其启动起来的。其 main 方法会先实例化一个 PMS 对象,然后调用 ServiceManager 的静态方法将其注册到 ServiceManager 中进行管理。

    public static PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {PackageManagerServiceCompilerMapping.checkProperties();PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);m.enableSystemUserPackages();ServiceManager.addService("package", m);final PackageManagerNative pmn = m.new PackageManagerNative();ServiceManager.addService("package_native", pmn);return m;}

当我们需要使用 PMS 解析 APK 的时候就会从 ServiceManager 中获取。

在 PMS 的构造方法中有许多工作要完成。一个 APK 安装的主要分成下面几个步骤,

  1. 拷贝文件到指定的目录:默认情况下,用户安装的 APK 首先会被拷贝到 /data/app 目录下,/data/app 目录是用户有权限访问的目录,在安装 APK 的时候会自动选择该目录存放用户安装的文件,而系统的 APK 文件则被放到了 /system 分区下,包括 /system/app/system/vendor/app,以及 /system/priv-app 等等,该分区只有 ROOT 权限的用户才能访问,这也就是为什么在没有 Root 手机之前,我们没法删除系统出场的 APP 的原因了。
  2. 解压缩 APK,拷贝文件,创建应用的数据目录:为了加快 APP 的启动速度,APK 在安装的时候,会首先将 APP 的可执行文件 dex 拷贝到 /data/dalvik-cache 目录,缓存起来。然后,在 /data/data/ 目录下创建应用程序的数据目录 (以应用的包名命名),存放在应用的相关数据,如数据库、XML 文件、Cache、二进制的 so 动态库等。
  3. 解析 APK 的 AndroidManifest.xml 文件。
    public PackageManagerService(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {// ....synchronized (mInstallLock) {synchronized (mPackages) {// Expose private service for system components to use.LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());sUserManager = new UserManagerService(context, this,new UserDataPreparer(mInsstaller, mInstallLock, mContext, mOnlyCore), mPackages);mPermissionManager = PermissionManagerService.create(context,new DefaultPermissionGrantedCallback() {@Overridepublic void onDefaultRuntimePermissionsGranted(int userId) {synchronized(mPackages) {mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);}}}, mPackages /*externalLock*/);mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);}}// ...mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context, "*dexopt*");DexManager.Listener dexManagerListener = DexLogger.getListener(this, installer, mInstallLock);mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock, dexManagerListener);mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);// ...synchronized (mInstallLock) {synchronized (mPackages) {// 创建消息mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);mHandlerThread.start();mHandler = new PackageHandler(mHandlerThread.getLooper());// ...// 扫描各个目录获取 APK 文件:VENDOR_OVERLAY_DIR           // framework 文件夹:frameworkDir// 系统文件夹:privilegedAppDir systemAppDir// 供应商的包:Environment.getVendorDirectory()// 原始设备制造商的包 :Environment.getOdmDirectory()// 原始设计商的包:Environment.getOdmDirectory()// 原始产品的包:// ....mInstallerService = new PackageInstallerService(context, this);final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr();if (instantAppResolverComponent != null) {mInstantAppResolverConnection = new InstantAppResolverConnection(mContext, instantAppResolverComponent.first,instantAppResolverComponent.second);mInstantAppResolverSettingsComponent =getInstantAppResolverSettingsLPr(instantAppResolverComponent.first);} else {mInstantAppResolverConnection = null;mInstantAppResolverSettingsComponent = null;}updateInstantAppInstallerLocked(null);final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();final int[] currentUserIds = UserManagerService.getInstance().getUserIds();for (int userId : currentUserIds) {userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());}mDexManager.load(userPackages);} // synchronized (mPackages)} // synchronized (mInstallLock)// ....}

在构造方法中会扫描多个目录来获取 APK 文件,上述注释中我们已经给出了这些目录,及其获取的方式。当扫描一个路径的时候会使用 scanDirLI() 方法来完成扫描工作。

    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {final File[] files = scanDir.listFiles();if (ArrayUtils.isEmpty(files)) {return;}try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) {int fileCount = 0;for (File file : files) {final boolean isPackage = (isApkFile(file) || file.isDirectory())&& !PackageInstallerService.isStageName(file.getName());if (!isPackage) {continue;}// 提交文件用来解析parallelPackageParser.submit(file, parseFlags);fileCount++;}for (; fileCount > 0; fileCount--) {// 获取解析的结果,即从队列阻塞队列中获取解析的结果ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();// ...if (throwable == null) {// TODO(toddke): move lower in the scan chain// Static shared libraries have synthetic package namesif (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {renameStaticSharedLibraryPackage(parseResult.pkg);}try {if (errorCode == PackageManager.INSTALL_SUCCEEDED) {scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags, currentTime, null);}} catch (PackageManagerException e) {errorCode = e.error;}}// 。。。}}}

从上面的代码中可以看出,提交文件来解析以及获取解析都是通过 ParallelPackageParser 来完成的。它使用 submit() 方法来提交文件用来解析,使用 take() 方法获取解析的结果。这两个方法的定义如下,

    public void submit(File scanFile, int parseFlags) {mService.submit(() -> {ParseResult pr = new ParseResult();try {PackageParser pp = new PackageParser();pp.setSeparateProcesses(mSeparateProcesses);pp.setOnlyCoreApps(mOnlyCore);pp.setDisplayMetrics(mMetrics);pp.setCacheDir(mCacheDir);pp.setCallback(mPackageParserCallback);pr.scanFile = scanFile;pr.pkg = parsePackage(pp, scanFile, parseFlags);} catch (Throwable e) {pr.throwable = e;}try {mQueue.put(pr);} catch (InterruptedException e) {Thread.currentThread().interrupt();mInterruptedInThread = Thread.currentThread().getName();}});}public ParseResult take() {try {if (mInterruptedInThread != null) {throw new InterruptedException("Interrupted in " + mInterruptedInThread);}return mQueue.take();} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new IllegalStateException(e);}}

submit() 方法使用一个线程池来执行任务,也就是上面的 mService。它会将要解析的信息封装成 PackageParser 对象,然后把解析的结果信息封装成 ParseResult 放进一个阻塞队列中。当调用 take() 方法的时候会从该阻塞队列中获取解析的结果。

包信息的解析最终是通过 PackageParser 的 parsePackage() 方法来完成的。其定义如下,

    public Package parsePackage(File packageFile, int flags, boolean useCaches)throws PackageParserException {Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;if (parsed != null) {return parsed;}long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;if (packageFile.isDirectory()) {parsed = parseClusterPackage(packageFile, flags);} else {// 是文件,所以走这条路线parsed = parseMonolithicPackage(packageFile, flags);}long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;cacheResult(packageFile, flags, parsed);return parsed;}

我们会在这方法中进入到 parseMonolithicPackage() 来对文件进行解析。

    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);try {// 解析final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);pkg.setCodePath(apkFile.getCanonicalPath());pkg.setUse32bitAbi(lite.use32bitAbi);return pkg;} catch (IOException e) {throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION);} finally {IoUtils.closeQuietly(assetLoader);}}

在这个方法中会使用 parseBaseApk() 来对 APK 文件进行解析,

    private Package parseBaseApk(File apkFile, AssetManager assets, int flags)throws PackageParserException {final String apkPath = apkFile.getAbsolutePath();String volumeUuid = null;if (apkPath.startsWith(MNT_EXPAND)) {final int end = apkPath.indexOf('/', MNT_EXPAND.length());volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);}mParseError = PackageManager.INSTALL_SUCCEEDED;mArchiveSourcePath = apkFile.getAbsolutePath();XmlResourceParser parser = null;try {final int cookie = assets.findCookieForPath(apkPath);// 读取 AndroidManifest.xmlparser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);final Resources res = new Resources(assets, mMetrics, null);final String[] outError = new String[1];// 在这里进一步解析 Manifest 的各种信息final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);pkg.setVolumeUuid(volumeUuid);pkg.setApplicationVolumeUuid(volumeUuid);pkg.setBaseCodePath(apkPath);pkg.setSigningDetails(SigningDetails.UNKNOWN);return pkg;} catch (PackageParserException e) {throw e;} catch (Exception e) {throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION);} finally {IoUtils.closeQuietly(parser);}}

这里的 ANDROID_MANIFEST_FILENAME 是一个字符串,这个字符串的定义是 AndroidManifest.xml,所以,我们找到了解析 Manifest 的地方。

然后方法会进入到 parseBaseApk() 方法中进一步对 Manifest 进行解析。其读取操作就是基本的 XML 解析的过程。它会使用内部定义的字符串常量从 Manifest 中获取应用的版本还有四大组件等信息。

解析完了 APK 之后会一路经过 return 语句返回到 scanDirLI() 方法中,当从阻塞队列中取出 Package 之后将会调用 scanPackageChildLI() 在该方法中会将解析的出的 APK 信息缓存到 PMS 中。

这样,在系统启动之后 PMS 就解析了全部的 APK 文件,并将其缓存到了 PMS 中。这样这些应用程序还无法展示给用户,所以需要 Launcher 桌面程序从 PMS 中获取安装包信息并展示到桌面上。

2、应用安装的过程

虽然 PMS 用来负责应用的安装和卸载,但是真实的工作却是交给 installd 来实现的。 installd 是在系统启动的时候,由 init 进程解析 init.rc 文件创建的。在早期版本的 Android 中,它使用 Socket 与 Java 层的 Installer 进行通信。在 9.0 的代码中,它使用 Binder 与 Java 层的 Installer 进行通信。当启动 Installd 的时候,将会调用其 main 方法,

int main(const int argc, char *argv[]) {return android::installd::installd_main(argc, argv);
}static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {int ret;int selinux_enabled = (is_selinux_enabled() > 0);setenv("ANDROID_LOG_TAGS", "*:v", 1);android::base::InitLogging(argv);SLOGI("installd firing up");union selinux_callback cb;cb.func_log = log_callback;selinux_set_callback(SELINUX_CB_LOG, cb);// 初始化全局信息if (!initialize_globals()) {exit(1);}// 初始化相关目录if (initialize_directories() < 0) {exit(1);}if (selinux_enabled && selinux_status_open(true) < 0) {exit(1);}if ((ret = InstalldNativeService::start()) != android::OK) {exit(1);}// 加入到 Binder 线程池当中IPCThreadState::self()->joinThreadPool();LOG(INFO) << "installd shutting down";return 0;
}

在启动 Installd 的时候会初始化各种相关的目录,这部分内容就不展开了。然后,它会调用 IPCThreadState::self()->joinThreadPool() 一行来将当前线程池加入到 Binder 线程池当中等待通信。

当 Java 层的 Installer 需要与之通信的时候,会调用 connect() 方法与之建立联系。其源码如下,这里会通过 ServiceManager 获取 installd 服务,然后将其转换成本地的服务进行 IPC 的调用。

    private void connect() {// 获取远程服务 IBinder binder = ServiceManager.getService("installd");if (binder != null) {try {binder.linkToDeath(new DeathRecipient() {@Overridepublic void binderDied() {connect();}}, 0);} catch (RemoteException e) {binder = null;}}if (binder != null) {// 转成本地服务进行 IPC 调用mInstalld = IInstalld.Stub.asInterface(binder);try {invalidateMounts();} catch (InstallerException ignored) {}} else {// 重连BackgroundThread.getHandler().postDelayed(() -> {connect();}, DateUtils.SECOND_IN_MILLIS);}}

Installer 与 PMC 类似,也是一种系统服务,它的启动的时刻与 PMS 基本一致,位于同一个方法中,并且其启动时刻位于 PMS 之前。

2、从 ADB 安装的过程

另外
有什么技术问题欢迎加我交流 qilebeaf
本人10多年大厂软件开发经验,精通Android,Java,Python,前端等开发,空余时间承接软件开发设计、课程设计指导、解决疑难bug、AI大模型搭建,AI绘图应用等。
欢迎砸单

相关文章:

Android应用安装过程

Android 系统源码源码-应用安装过程 Android 中应用安装的过程就是解析 AndroidManifest.xml 的过程&#xff0c;系统可以从 Manifest 中得到应用程序的相关信息&#xff0c;比如 Activity、Service、Broadcast Receiver 和 ContentProvider 等。这些工作都是由 PackageManage…...

Word中输入文字时,后面的文字消失

当在Word中输入文字时&#xff0c;如果发现后面的文字消失&#xff0c;通常是由以下3个原因造成的&#xff1a; 检查Insert键状态&#xff1a;首先确认是否误按了Insert键。如果是&#xff0c;请再次按下Insert键以切换回插入模式。在插入模式下&#xff0c;新输入的文字会插入…...

【LeetCode】合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 解题思路 水题&#xff0c;主要用于后面的链表的归并排序做了该题 AC代码 # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nex…...

分子AI预测赛Task1笔记

分子AI预测赛Task1笔记 实践步骤&#xff1a;跑通baseline → 尝试个人idea→尝试进阶baseline 一、跑通baseline 1、应当先下载数据库 下载相应的数据库 !pip install lightgbm openpyxl2、训练模型并预测结果 首先要导入相应的库和方法类&#xff0c;如pandas等 # 1. …...

ubuntu 安装并启用 samba

环境&#xff1a;ubuntu server 24.04 步骤如下&#xff1a; sudo apt update sudo apt install samba修改配置文件&#xff1a; sudo vi /etc/samba/smb.conf新增内容&#xff1a; [username]path /home/[username]available yesvalid users [username]read only nobrow…...

atcoder ABC 357-D题详解

atcoder ABC 357-D题详解 Problem Statement For a positive integer N, let VN​ be the integer formed by concatenating N exactly N times. More precisely, consider N as a string, concatenate N copies of it, and treat the result as an integer to get VN​. For…...

从单一到多元:EasyCVR流媒体视频汇聚技术推动安防监控智能升级

随着科技的飞速发展&#xff0c;视频已成为我们日常生活和工作中的重要组成部分。尤其在远程办公、在线教育、虚拟会议等领域&#xff0c;视频的应用愈发广泛。为了满足日益增长的视频需求&#xff0c;流媒体视频汇聚融合技术应运而生&#xff0c;它不仅改变了传统视频的观看和…...

Spring MVC数据绑定和响应——数据回写(二)JSON数据的回写

项目中已经导入了Jackson依赖&#xff0c;可以先调用Jackson的JSON转换的相关方法&#xff0c;将对象或集合转换成JSON数据&#xff0c;然后通过HttpServletResponse将JSON数据写入到输出流中完成回写&#xff0c;具体步骤如下。 1、修改文件DataController.java&#xff0c;在…...

怎么快速给他人分享图片?扫描二维码看图的简单做法

现在通过二维码来查看图片是一种很常见的方法&#xff0c;通过二维码来查看图片不仅能够减少对手机存储空间的占用&#xff0c;而且获取图片变得更加方便快捷&#xff0c;只需要扫码就能够查看图片&#xff0c;有利于图片的展现。很多的场景中都有图片二维码的应用&#xff0c;…...

【UML用户指南】-26-对高级行为建模-状态图

目录 1、概念 2、组成结构 3、一般用法 4、常用建模技术 4.1、对反应型对象建模 一个状态图显示了一个状态机。在为对象的生命期建模中 活动图展示的是跨过不同的对象从活动到活动的控制流 状态图展示的是单个对象内从状态到状态的控制流。 在UML中&#xff0c;用状态图…...

解决VSCode无法用ssh连接远程服务器的问题

原因&#xff1a; 因为windows自带的ssh无法连接远程服务器&#xff0c;需要用git底下的ssh.exe。 搜了很久&#xff0c;试过很多方法&#xff0c;包括替换掉环境变量中的ssh&#xff0c;但是都无效&#xff0c;最后发现是要在VSCode中配置需要使用哪个ssh.exe。 步骤&#…...

【区块链+基础设施】银联云区块链服务 | FISCO BCOS应用案例

为了顺应区块链基础设施化的发展趋势&#xff0c;中国银联推出了银联云区块链服务——UPBaaS&#xff0c;为金融行业采用区块链 技术提出了解决方案&#xff0c;微众银行为平台提供 FISCO BCOS 区块链开源技术支持。通过银联云区块链服务&#xff0c;用户可 以用可视化的方式创…...

Java SE入门及基础(61) 死锁 死锁发生条件

目录 死锁 1. 死锁的概念 2. 死锁发生条件 互斥条件 不可剥夺条件 请求与保持条件 循环等待 3. 案例分析 示例 分析 死锁 1. 死锁的概念 Deadlock describes a situation where two or more threads are blocked forever, waiting for each other 死锁描述了一种情…...

简单爬虫案例——爬取快手视频

网址&#xff1a;aHR0cHM6Ly93d3cua3VhaXNob3UuY29tL3NlYXJjaC92aWRlbz9zZWFyY2hLZXk9JUU2JThCJTg5JUU5JTlEJUEy 找到视频接口&#xff1a; 视频链接在photourl中 完整代码&#xff1a; import requestsimport re url https://www.kuaishou.com/graphql cookies {did: web_…...

42、nginx之nginx.conf

nginx----web服务器 一、nginx http就是apache&#xff0c;在国内很少。 nginx是开源的&#xff0c;是一款高性能&#xff0c;轻量级的web服务软件。 稳定性高&#xff0c;而且版本迭代比较快&#xff08;修复bug速度比较快&#xff0c;安全性快&#xff09; 消耗系统资源…...

高薪程序员必修课-java为什么要用并发编程

目录 前言 1. 提高性能和效率 2. 更好地响应用户 3. 优化I/O操作 具体示例 示例1&#xff1a;提高性能和效率 示例2&#xff1a;更好地响应用户 示例3&#xff1a;优化I/O操作 总结 前言 并发编程允许多个线程在同一时间执行任务。下面我们从多个原理角度来解释为什么J…...

postgreSQL学习

postgreSql学习 学习参考&#xff1a;1、命令1.1 登录1.2 关闭连接 2、常用数据类型2.1 数值类型2.2 字符串类型2.3 时间2.4 其他 3、自增主键4、sql4.1 库操作&#xff08;1&#xff09;创建新库&#xff08;2&#xff09;切换数据库&#xff08;3&#xff09;删库【谨慎&…...

【3】系统标定

文章目录 雷达标定相机主雷达标定底盘动力学标定车辆循迹验证建图 雷达标定 主要是为了获得到lidar到imu的tf关系。imu为父坐标lidar为子坐标。其他雷达标定到主lidar坐标系下。 标定的结果都是生成一个是四元数。 #mermaid-svg-crOWRnT4UE0jtJVy {font-family:"trebuch…...

网安小贴士(3)网安协议

一、前言 网络安全协议是构建安全网络环境的基础&#xff0c;它们帮助保护网络通信免受各种威胁和攻击。 二、定义 网络安全协议是指在计算机网络中用于确保网络通信和数据传输安全的协议。它们定义了在网络通信过程中的安全机制、加密算法、认证和授权流程等&#xff0c;以保…...

大数据面试题之HBase(1)

目录 介绍下HBase HBase优缺点 说下HBase原理 介绍下HBase架构 HBase读写数据流程 HBase的读写缓存 在删除HBase中的一个数据的时候&#xff0c;它什么时候真正的进行删除呢?当你进行删除操作&#xff0c;它是立马就把数据删除掉了吗? HBase中的二级索引 HBa…...

git回退commit的方式

在Git中&#xff0c;回退commit&#xff08;即撤销之前的提交&#xff09;可以通过多种方式来实现。以下是一些常见的方法&#xff0c;以及它们的详细步骤和注意事项&#xff1a; ### 1. 使用git revert命令 git revert命令用于撤销某次commit&#xff0c;但它并不会删除该comm…...

[Information Sciences 2023]用于假新闻检测的相似性感知多模态提示学习

推荐的一个视频&#xff1a;p-tuning P-tunning直接使用连续空间搜索 做法就是直接将在自然语言中存在的词直接替换成可以直接训练的输入向量。本身的Pretrained LLMs 可以Fine-Tuning也可以不做。 这篇论文也解释了为什么很少在其他领域结合知识图谱的原因&#xff1a;就是因…...

自定义vue3 hooks

文章目录 hooks目录结构demo hooks 当页面内有很多的功能&#xff0c;js代码太多&#xff0c;不好维护&#xff0c;可以每个功能都有写一个js或者ts&#xff0c;这样的话&#xff0c;代码易读&#xff0c;并且容易维护&#xff0c;组合式setup写法与此结合&#x1f44d;&#…...

《昇思25天学习打卡营第21天 | 昇思MindSporePix2Pix实现图像转换》

21天 本节学习了通过Pix2Pix实现图像转换。 Pix2Pix是基于条件生成对抗网络&#xff08;cGAN&#xff09;实现的一种深度学习图像转换模型。可以实现语义/标签到真实图片、灰度图到彩色图、航空图到地图、白天到黑夜、线稿图到实物图的转换。Pix2Pix是将cGAN应用于有监督的图…...

【文档+源码+调试讲解】科研经费管理系统

目 录 目 录 摘 要 ABSTRACT 1 绪论 1.1 课题背景 1.2 研究现状 1.3 研究内容 2 系统开发环境 2.1 vue技术 2.2 JAVA技术 2.3 MYSQL数据库 2.4 B/S结构 2.5 SSM框架技术 3 系统分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 操作可行性 3.1.3 经济可行性 3.1…...

linux 下 rm 为什么要这么写?

下面代码中的rm 为什么要写成/bin/rm? 大文件清理&#xff0c;高宿主含量样本可节约>90%空间/bin/rm -rf temp/qc/*contam* temp/qc/*unmatched* temp/qc/*.fqls -l temp/qc/ 这是一个很好的问题&#xff0c;观察很仔细, 也带着了自己的思考。 rm是 Linux 下的一个危险…...

【Spring Boot】Spring AOP中的环绕通知

目录 一、什么是AOP?二、AOP 的环绕通知2.1 切点以及切点表达式2.2 连接点2.3 通知&#xff08;Advice&#xff09;2.4 切面(Aspect)2.5 不同通知类型的区别2.5.1 正常情况下2.5.2异常情况下 2.6 统一管理切点PointCut 一、什么是AOP? Aspect Oriented Programming&#xff…...

docker部署前端,配置域名和ssl

之前使用80端口部署前端项目后&#xff0c;可以使用IP端口号在公网访问到部署的项目。 进行ICP域名备案后&#xff0c;可以通过域名解析将IP套壳&#xff0c;访问域名直接访问到部署的项目~ 如果使用http协议可以很容易实现这个需求&#xff0c;对nginx.conf文件进行修改&#…...

初学Spring之 IOC 控制反转

Spring 是一个轻量级的控制反转&#xff08;IOC&#xff09;和面向切面编程&#xff08;AOP&#xff09;的框架 导入 jar 包&#xff1a;spring-webmvc、spring-jdbc <dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc&…...

rpc的仅有通信的功能,在网断的情况下,比网通情况下,内存增长会是什么原因

RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;主要负责在分布式系统中透明地调用远程服务&#xff0c;就像调用本地函数一样。它封装了网络通信的细节&#xff0c;使得开发者可以专注于业务逻辑而非底层通信协议。RPC通信通常包括序列化、网络传输…...

从零开始:如何设计一个现代化聊天系统

写在前面: 此博客内容已经同步到我的博客网站,如需要获得更优的阅读体验请前往https://mainjaylai.github.io/Blog/blog/system/chat-system 在当今数字化时代,聊天系统已成为我们日常生活和工作中不可或缺的一部分。从个人交流到团队协作,从客户服务到社交网络,聊天应用…...

香橙派OrangePi AIpro初体验:当小白拿到一块开发板第一时间会做什么?

文章目录 香橙派OrangePi AIpro初体验&#xff1a;当小白拿到一块高性能AI开发板第一时间会做什么前言一、香橙派OrangePi AIpro概述1.简介2.引脚图开箱图片 二、使用体验1.基础操作2.软件工具分析 三、香橙派OrangePi AIpro.测试Demo1.测试Demo1&#xff1a;录音和播音(USB接口…...

【C语言内存函数】

目录 1.memcpy 使用 模拟实现 2.memmove 使用 模拟实现 3.memset 使用 4.memcmp 使用 1.memcpy 使用 void * memcpy ( void * destination, const void * source, size_t num );目的地址 源地址 字节数 destination&#xff1a;指向要复制内…...

Mysql部署MHA高可用

部署前准备&#xff1a; mysql-8.0.27下载地址&#xff1a;https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.27-1.el7.x86_64.rpm-bundle.tar mha-manager下载地址&#xff1a;https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-mana…...

【算法学习】射线法判断点在多边形内外(C#)以及确定内外两点连线与边界的交点

1.前言&#xff1a; 在GIS开发中&#xff0c;经常会遇到确定一个坐标点是否在一块区域的内部这一问题。 如果这个问题不是一个单纯的数学问题&#xff0c;例如&#xff1a;在判断DEM、二维图像像素点、3D点云点等含有自身特征信息的这些点是否在一个区域范围内部的时候&#x…...

SQL语句(DML)

DML英文全称是Data Manipulation Language&#xff08;数据操作语言&#xff09;&#xff0c;用来对数据库中表的数据记录进行增删改等操作 DML-添加数据 insert into employee(id, workno, name, gender, age, idcard) values (1,1,Itcast,男,10,123456789012345678);select *…...

uniapp小程序打开地图导航

uniapp uni.getLocation({type: gcj02, //返回可以用于uni.openLocation的经纬度success: function (res) {const latitude res.latitude;const longitude res.longitude;uni.openLocation({latitude: latitude,longitude: longitude,success: function () {console.log(suc…...

webstorm格式化或保存时 vue3引入的组件被删除了

解决办法 保存时设置 格式化设置...

Java时间转换

一、线程不安全 Date date new Date(); SimpleDateFormat dateFormat new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String prefix dateFormat.format(date);二、线程安全,建议使用 String t1 LocalDateTime.now().format(DateTimeFormatter.ofPattern("y…...

Spring Boot与WebFlux的实战案例

Spring Boot与WebFlux的实战案例 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天&#xff0c;我们将探讨如何利用Spring Boot和WebFlux构建响应式应用的实战…...

vue3引入本地静态资源图片

一、单张图片引入 import imgXX from /assets/images/xx.png二、多张图片引入 说明&#xff1a;import.meta.url 是一个 ESM 的原生功能&#xff0c;会暴露当前模块的 URL。将它与原生的 URL 构造器 组合使用 注意&#xff1a;填写自己项目图片存放的路径 /** vite的特殊性…...

git 禁止dev合并到任何其他分支

创建 pre-merge-commit 钩子 导航到 Git 仓库的钩子目录&#xff1a; cd /path/to/your/repo/.git/hooks创建或编辑 pre-merge-commit 钩子&#xff1a; 也可以通过指令创建 nano pre-merge-commit在钩子文件中添加以下代码&#xff1a; #!/bin/sh# 获取当前分支名称 curr…...

第二节:如何使用thymeleaf渲染html(自学Spring boot 3.x的第一天)

大家好&#xff0c;我是网创有方&#xff0c;今天来学习如何使用thymeleaf渲染html。该模板运用不广泛&#xff0c;所以本节内容了解既可。 第一步&#xff1a;创建html文件。 在模板templates目录下创建一个html文件。 编写代码如下&#xff1a; <!DOCTYPE html> <…...

计算机相关术语科普之什么叫网关(Gateway)

网关&#xff08;Gateway&#xff09;是一个在计算机网络中起到关键作用的设备或系统&#xff0c;它扮演着网络间连接器或协议转换器的角色。 一、定义与功能 1&#xff09;定义&#xff1a; 网关是在不同网络之间实现互连的复杂设备&#xff0c;仅用于两个高层协议不同的网…...

B站网页部分API

https://www.bilibili.com/ 数据结构 mid: 用户id name: 用户名 face: 用户头像url noface.jpg为默认头像 sign&#xff1a; 签名level: b站等级 coins: b站硬币粉丝 https://api.bilibili.com/x/relation/fans?vmid{mid}&pn{pn}&ps{limit}&orderdesc&…...

使用Spring Boot和Spring Security保护你的应用

使用Spring Boot和Spring Security保护你的应用 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将深入探讨如何利用Spring Boot和Spring Security来保护…...

CVE-2019-12272 Openwrt可视页面LuCi命令注入漏洞复现(完结)

声明 本文所使用的一些源代码等内容已经上传至github&#xff0c;具体地址如下 Vulnerability_POC-EXP/OpenWrt/CVE-2019-12272 at main a2148001284/Vulnerability_POC-EXP GitHub 漏洞简介 参考内容&#xff1a; CVE-2019-12272 OpenWrt图形化管理界面LuCI命令注入分析 |…...

【多线程开发 4】从源码学习LockSupport

从源码学习LockSupport 2024年6月30日 大家好啊&#xff0c;好久没写博客了&#xff0c;今天打算写一下&#xff0c;讲一下JUC里面LockSupport这个类。 这个是一个工具类&#xff0c;实际上也是为了线程通信开发的。它的源码比较短&#xff0c;也只引用了Unsafe一个类。所以…...

gameui C++的代码

gameui C的代码 #include <graphics.h> #include "gameboard.h" const int WIDTH 560; const int HEIGHT 780; const int GRID_SIZE 120; class GameUi { private: public:GameUi(GameBoard& gb) {// 初始化图形窗口initgraph(WIDTH, HEIGHT);// 设置…...

1.什么是js?特点是什么?组成部分?

Js是一种直译式脚本语言&#xff0c;一种动态类型&#xff0c;弱类型&#xff0c;基于原型的高级语言。 直译式&#xff1a;js程序运行过程中直接编译成机器语言。 脚本语言&#xff1a;在程序运行过程中逐行进行解释说明&#xff0c;不需要预编译。 动态类型&#xff1a;js…...

Promethuse-监控 Etcd

一、思路 Prometheus监控Etcd集群&#xff0c;是没有对应的exporter&#xff0c;而 由CoreOS公司开发的Operator&#xff0c;用来扩展 Kubernetes API&#xff0c;特定的应用程序控制器&#xff0c;它用来创建、配置和管理复杂的有状态应用&#xff0c;如数据库、缓存和监控系…...

亚马逊云服务器的价格真的那么贵吗?一年要花多少钱?

亚马逊Web服务&#xff08;AWS&#xff09;作为全球领先的云计算平台&#xff0c;其定价策略常常引起用户的关注。很多人可能会问&#xff1a;"AWS真的那么贵吗&#xff1f;"实际上&#xff0c;这个问题的答案并不是简单的"是"或"否"&#xff0c…...

探索 Apache Paimon 在阿里智能引擎的应用场景

摘要&#xff1a;本文整理自Apache Yarn && Flink Contributor&#xff0c;阿里巴巴智能引擎事业部技术专家王伟骏&#xff08;鸿历&#xff09;老师在 5月16日 Streaming Lakehouse Meetup Online 上的分享。内容主要分为以下三个部分&#xff1a; 一、 阿里智能引擎…...

软件开发中常用的11款bug记录、跟踪、管理系统对比【2024更新】

软件开发项目的复杂性不断增加&#xff0c;有效的bug管理变得尤为关键。对开发团队而言&#xff0c;没有什么比选择一款合适的Bug跟踪工具更重要的了。工具的功能、界面友好度、整合能力及成本都是决策的关键因素。 1、PingCode 推荐指数&#xff1a;五星 简介&#xff1a;P…...

高考服务系统

摘 要 每年有大批考生在进行填写高考志愿时并不很清楚自己的高考分数适合那些高校以及专业。高考考生面临着未被高校录取&#xff0c;被调剂专业&#xff0c;甚至可能复读的问题。若能让考生轻松查询到高校录取、高校专业、高校招生等相关信息&#xff0c;能减少很大一部分考生…...

深度学习:C++和Python如何对大图进行小目标检测

最近在医美和工业两条线来回穿梭&#xff0c;甚是疲倦&#xff0c;一会儿搞搞医美的人像美容&#xff0c;一会儿搞搞工业的检测&#xff0c;最近新接的一个项目&#xff0c;关于瑕疵检测的&#xff0c;目标图像也并不是很大吧&#xff0c;需要放大后&#xff0c;才能看见细小的…...

预计11月上市第七代宝马M5开始量产

日前,车质网从宝马官方获悉,第七代宝马M5已经在德国丁戈尔芬工厂量产。新车底盘代号为G90,海外起售价约合87万人民币,预计11月全球上市。外观方面,新车采用了宝马最新的家族式设计语言,经典的“双肾”格栅内部加入了黑化处理,并且格栅的轮廓增加了LED灯带,点亮后很有辨…...

固态电池量产加速,我们离电动车新时代还有多远?

随着一则确定性很强的不确定性消息不胫而走,固态电池又一次成为目光焦点!5月29日,《中国日报》报道称,中国或投入约60亿元,鼓励有条件的企业对全固态电池相关技术开展研发,宁德时代、比亚迪、一汽、上汽、卫蓝新能源和吉利共六家企业或获得政府基础研发支持。多位知情人士…...

沃尔沃S90:从设计到实践的安全标杆

“网上没赢过,实战没输过”,沃尔沃用每一次安全事故生动诠释了这句话。虽然包括沃尔沃品牌本身和车主在内,平时都比较低调,是“车圈逐渐粉圈化”大环境下的一股清流,但真正遇到交通事故的时候,你才知道,沃尔沃是真能保命啊。这不,最近就有位沃尔沃S90车主在高速上以100…...

基于文本来推荐相似酒店

基于文本来推荐相似酒店 查看数据集基本信息 import pandas as pd import numpy as np from nltk.corpus import stopwords from sklearn.metrics.pairwise import linear_kernel from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extrac…...

【Clickhouse】DB::Exception: Table is in readonly mode问题解决

1. 问题描述 公司电力检修&#xff0c;机房断电再上电之后clickhouse的一张表出现了只读现象 2024.05.20 16:58:08.702472 [ 173655 ] {17C25AD8068BBE9A} <Error> executeQuery: Code: 242, e.displayText() DB::Exception: Table is in readonly mode (version 20.5…...

一图读懂足坛反腐系列案时间线

【来源:检察日报正义网(ID:JCRB_ZYW)】...