【QT】Qt Application Manager启动应用源码分析
Qt Application Manager启动应用源码分析
- Qt Application Manager(以下简称QTAM)是QT推出的一款应用管理程序,可以把它简单理解成Android的Launcher+SystemUI。但是,QTAM又集成了Wayland功能,并且自身实现了一套Compositor。QTAM以多进程启动情况下(默认),其地位相当于 Launcher+SystemUI+Compositor。
- 关于QTAM的基本介绍可以参考《Qt Application Manager简介》
启用应用
-
QTAM作为一款应用管理程序,适用于嵌入式端(如车载)应用。利用QT开发的应用程序只需要简单适配一下QTAM的规范,可以大幅度减少开发一套应用管理程序的成本。同时QTAM支持QML方式。应用管理最基本的功能,是应用的启动。下面基于QTAM 6.2.2版本进行分析。
-
启动应用的接口ApplicationManager:: startApplication,该接口可以以C++或QML的形式调用(下述摘取了部分源码)
// src\manager-lib\applicationmanager.h
class ApplicationManager : public QAbstractListModel
{Q_OBJECTQ_CLASSINFO("D-Bus Interface", "io.qt.ApplicationManager")Q_CLASSINFO("AM-QmlType", "QtApplicationManager.SystemUI/ApplicationManager 2.0 SINGLETON")public:// 通过C++和QML互相调用的方式,QML端也可以调用到该接口。// 关于QML调用C++,网上文章比较多可自行百度。Q_SCRIPTABLE bool startApplication(const QString &id, const QString &documentUrl = QString());
}// src\manager-lib\applicationmanager.cpp
bool ApplicationManager::startApplication(const QString &id, const QString &documentUrl)
{try {return startApplicationInternal(id, documentUrl);} catch (const Exception &e) {qCWarning(LogSystem) << e.what();return false;}
}// src\manager-lib\applicationmanager.cpp
bool ApplicationManager::startApplicationInternal(const QString &appId, const QString &documentUrl,const QString &documentMimeType,const QString &debugWrapperSpecification,const QVector<int> &stdioRedirections) Q_DECL_NOEXCEPT_EXPR(false)
{// 根据appid,获取到应用信息。appid唯一标识应用Application *app = fromId(appId);// 获取应用的RunTime,Runtime可以理解为应用运行时。QTAM提供多种Runtime,比如NativeRuntime、QML Runtime等。AbstractRuntime *runtime = app->currentRuntime();auto runtimeManager = runtime ? runtime->manager() : RuntimeFactory::instance()->manager(app->runtimeName());if (!runtimeManager)throw Exception("No RuntimeManager found for runtime: %1").arg(app->runtimeName());// 判断QtAM运行的是多进程模式,还是单进程模式(默认为多进程模式,即每个应用以单独的进程启动)bool inProcess = runtimeManager->inProcess();// 判断当前rimtime的状态。 应用的状态为 StartingUp-> Running -> ShuttingDown -> NotRunning// 第一次启动时,这里为NotRunningif (runtime) {switch (runtime->state()) {case Am::StartingUp:case Am::Running:if (!debugWrapperCommand.isEmpty()) {throw Exception("Application %1 is already running - cannot start with debug-wrapper: %2").arg(app->id(), debugWrapperSpecification);}// documentUrl为应用入口。比如一个QML文件。if (!documentUrl.isNull())runtime->openDocument(documentUrl, documentMimeType);else if (!app->documentUrl().isNull())runtime->openDocument(app->documentUrl(), documentMimeType);// 激活AppemitActivated(app);return true;case Am::ShuttingDown:return false;case Am::NotRunning:break;}}// container指应用运行上下文(Context)AbstractContainer *container = nullptr;QString containerId;// 如果是多进程模式if (!inProcess) {if (d->containerSelectionConfig.isEmpty()) {// 默认使用ProcessContainercontainerId = qSL("process");} else {// check config filefor (const auto &it : qAsConst(d->containerSelectionConfig)) {const QString &key = it.first;const QString &value = it.second;bool hasAsterisk = key.contains(qL1C('*'));if ((hasAsterisk && key.length() == 1)|| (!hasAsterisk && key == app->id())|| QRegularExpression(QRegularExpression::wildcardToRegularExpression(key)).match(app->id()).hasMatch()) {containerId = value;break;}}}if (d->containerSelectionFunction.isCallable()) {QJSValueList args = { QJSValue(app->id()), QJSValue(containerId) };containerId = d->containerSelectionFunction.call(args).toString();}if (!ContainerFactory::instance()->manager(containerId))throw Exception("No ContainerManager found for container: %1").arg(containerId);}bool attachRuntime = false;if (!runtime) {if (!inProcess) {// 快启动模式,可以理解为预先启动几个(有上限)的Runtime+Contianer,预先加载了一些资源。if (QuickLauncher::instance()) {// 该情况比较特殊,不考虑。}if (!container) {// 创建Containercontainer = ContainerFactory::instance()->create(containerId, app, stdioRedirections,debugEnvironmentVariables, debugWrapperCommand);} else {container->setApplication(app);}if (!container) {qCCritical(LogSystem) << "ERROR: Couldn't create Container for Application (" << app->id() <<")!";return false;}if (runtime)attachRuntime = true;}if (!runtime)runtime = RuntimeFactory::instance()->create(container, app);if (runtime)emit internalSignals.newRuntimeCreated(runtime);}if (!runtime) {qCCritical(LogSystem) << "ERROR: Couldn't create Runtime for Application (" << app->id() <<")!";return false;}// 绑定信号与槽。监听应用状态变化。connect(runtime, &AbstractRuntime::stateChanged, this, [this, app](Am::RunState newRuntimeState) {app->setRunState(newRuntimeState);emit applicationRunStateChanged(app->id(), newRuntimeState);emitDataChanged(app, QVector<int> { IsRunning, IsStartingUp, IsShuttingDown });});// 加载应用入口。if (!documentUrl.isNull())runtime->openDocument(documentUrl, documentMimeType);else if (!app->documentUrl().isNull())runtime->openDocument(app->documentUrl(), documentMimeType);if (inProcess) {// 如果是单进程模式(以线程方式启动应用)bool ok = runtime->start();if (ok)emitActivated(app);elseruntime->deleteLater();return ok;} else {// We can only start the app when both the container and the windowmanager are ready.// Using a state-machine would be one option, but then we would need that state-machine// object plus the per-app state. Relying on 2 lambdas is the easier choice for now.// 多进程模式下,QTAM需要完成了Compositor初始化,才可以启动应用。auto doStartInContainer = [this, app, attachRuntime, runtime]() -> bool {// 首次启动应用默认走 runtime->start()bool successfullyStarted = attachRuntime ? runtime->attachApplicationToQuickLauncher(app): runtime->start();if (successfullyStarted)emitActivated(app);elseruntime->deleteLater(); // ~Runtime() will clean app->nonAliased()->m_runtimereturn successfullyStarted;};auto tryStartInContainer = [container, doStartInContainer]() -> bool {if (container->isReady()) {// Since the container is already ready, start the app immediatelyreturn doStartInContainer();} else {// We postpone the starting of the application to a later point in time,// since the container is not ready yet
# if defined(Q_CC_MSVC)qApp->connect(container, &AbstractContainer::ready, doStartInContainer); // MSVC cannot distinguish between static and non-static overloads in lambdas
# elseconnect(container, &AbstractContainer::ready, doStartInContainer);
#endifreturn true;}};if (isWindowManagerCompositorReady()) {return tryStartInContainer();} else {connect(this, &ApplicationManager::windowManagerCompositorReadyChanged, tryStartInContainer);return true;}}
}
- 上面的代码中,主要就是通过AppID获取应用对象(包含应用信息)、创建应用Runtime、创建应用Container(Context)、加载应用入口(比如QML文件)、调用RunTime启动应用。加载应用入口文件,实际上会调用QML引擎解析QML文件。这里主要关注RunTime如何将应用进程启动。
- 多进程默认情况下,走NativeRuntime。
// src\manager-lib\nativeruntime.cpp
bool NativeRuntime::start()
{// 首次启动时,状态为 Am::NotRunningswitch (state()) {case Am::StartingUp:case Am::Running:return true;case Am::ShuttingDown:return false;case Am::NotRunning:break;}// 初始化OpenGL配置if (m_app)openGLConfig = m_app->info()->openGLConfiguration();if (openGLConfig.isEmpty())openGLConfig = manager()->systemOpenGLConfiguration();if (!openGLConfig.isEmpty())uiConfig.insert(qSL("opengl"), openGLConfig);// 获取IconQString iconThemeName = manager()->iconThemeName();QStringList iconThemeSearchPaths = manager()->iconThemeSearchPaths();if (!iconThemeName.isEmpty())uiConfig.insert(qSL("iconThemeName"), iconThemeName);if (!iconThemeSearchPaths.isEmpty())uiConfig.insert(qSL("iconThemeSearchPaths"), iconThemeSearchPaths);QVariantMap config = {{ qSL("logging"), loggingConfig },{ qSL("baseDir"), QDir::currentPath() },{ qSL("runtimeConfiguration"), configuration() },{ qSL("securityToken"), qL1S(securityToken().toHex()) },{ qSL("dbus"), dbusConfig }};if (!m_startedViaLauncher && !m_isQuickLauncher)config.insert(qSL("systemProperties"), systemProperties());if (!uiConfig.isEmpty())config.insert(qSL("ui"), uiConfig);QMap<QString, QString> env = {{ qSL("QT_QPA_PLATFORM"), qSL("wayland") },{ qSL("QT_IM_MODULE"), QString() }, // Applications should use wayland text input{ qSL("QT_SCALE_FACTOR"), QString() }, // do not scale wayland clients{ qSL("AM_CONFIG"), QString::fromUtf8(QtYaml::yamlFromVariantDocuments({ config })) },{ qSL("QT_WAYLAND_SHELL_INTEGRATION"), qSL("xdg-shell")},};// 判断DLT(一种日志服务)是否开启。if (!Logging::isDltEnabled()) {// sadly we still need this, since we need to disable DLT as soon as possibleenv.insert(qSL("AM_NO_DLT_LOGGING"), qSL("1"));}// 获取环境变量(QT也有自己的一套环境变量)for (QMapIterator<QString, QVariant> it(configuration().value(qSL("environmentVariables")).toMap()); it.hasNext(); ) {it.next();if (!it.key().isEmpty())env.insert(it.key(), it.value().toString());}QStringList args;if (!m_startedViaLauncher) {args.append(variantToStringList(m_app->runtimeParameters().value(qSL("arguments"))));// 获取启动参数if (!m_document.isNull())args << qSL("--start-argument") << m_document;// 如果DLT没有开启的话if (!Logging::isDltEnabled())args << qSL("--no-dlt-logging");} else {if (m_isQuickLauncher)args << qSL("--quicklaunch");args << QString::fromLocal8Bit(ProcessTitle::placeholderArgument); // must be last argument}emit signaler()->aboutToStart(this);// 调用Container,启动应用m_process = m_container->start(args, env, config);if (!m_process)return false;// 绑定信号,获得应用状态。QObject::connect(m_process, &AbstractContainerProcess::started,this, &NativeRuntime::onProcessStarted);QObject::connect(m_process, &AbstractContainerProcess::errorOccured,this, &NativeRuntime::onProcessError);QObject::connect(m_process, &AbstractContainerProcess::finished,this, &NativeRuntime::onProcessFinished);// 到此默认认为应用已经启动。通过上面的三个绑定信号操作,可以更新状态。setState(Am::StartingUp);return true;
}
- Runtime调用Container启动应用,默认情况下走ProcessContainer,这里会调用QProcess创建出应用进程。
// src\manager-lib\processcontainer.cpp
AbstractContainerProcess *ProcessContainer::start(const QStringList &arguments,const QMap<QString, QString> &runtimeEnvironment,const QVariantMap &amConfig)
{// m_program 是 Appman 这个二进制程序。是QTAM提供的用来加载应用的程序。if (!QFile::exists(m_program)) {qCWarning(LogSystem) << "Program" << m_program << "not found";return nullptr;}// 创建HostProcess,通过它创建出进程。HostProcess *process = new HostProcess();process->setWorkingDirectory(m_baseDirectory);process->setProcessEnvironment(penv);process->setStopBeforeExec(configuration().value(qSL("stopBeforeExec")).toBool());process->setStdioRedirections(m_stdioRedirections);QString command = m_program;QStringList args = arguments;if (!m_debugWrapperCommand.isEmpty()) {auto cmd = DebugWrapper::substituteCommand(m_debugWrapperCommand, m_program, arguments);command = cmd.takeFirst();args = cmd;}qCDebug(LogSystem) << "Running command:" << command << "arguments:" << args;// 实际上,第一个参数是Appman这个二进程程序的路径。// 例如: /system/bin/Appman// 调用HostProcess启动应用process->start(command, args);m_process = process;setControlGroup(configuration().value(qSL("defaultControlGroup")).toString());return process;
}// src\manager-lib\processcontainer.cpp
void HostProcess::start(const QString &program, const QStringList &arguments)
{// 绑定各种状态信号connect(m_process, &QProcess::started, this, [this]() {// we to cache the pid in order to have it available after the process crashedm_pid = m_process->processId();emit started();});connect(m_process, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {emit errorOccured(static_cast<Am::ProcessError>(error));});connect(m_process, static_cast<void (QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished),this, [this](int exitCode, QProcess::ExitStatus exitStatus) {emit finished(exitCode, static_cast<Am::ExitStatus>(exitStatus));});connect(m_process, &QProcess::stateChanged,this, [this](QProcess::ProcessState newState) {emit stateChanged(static_cast<Am::RunState>(newState));});#if defined(Q_OS_UNIX)// make sure that the redirection fds do not have a close-on-exec flag, since we need them// in the child process.for (int fd : qAsConst(m_stdioRedirections)) {if (fd < 0)continue;int flags = fcntl(fd, F_GETFD);if (flags & FD_CLOEXEC)fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC);}
#endif// QProcess *m_process;// 调用QProcess的start函数,这个类会根据入参启动进程。// 参数Program是 Appman这个二进制程序。// 参数arguments作为入参,传给Appman这个二进制程序。// 到此应用进程就启动起来了。m_process->start(program, arguments);#if defined(Q_OS_UNIX)// we are forked now and the child process has received a copy of all redirected fds// now it's time to close our fds, since we don't need them anymore (plus we would block// the tty where they originated from)for (int fd : qAsConst(m_stdioRedirections)) {if (fd >= 0)::close(fd);}
#endif
}
- 上述代码中,实际上利用了QProcess这个,将Appman(二进制程序)和入参(比如应用的QML启动文件)作为参数。通过QProcess创建了新的进程,启动了应用程序。
从上述代码中,可以看出QTAM多进程模式下,是通过QProcess创建了子进程来加载AppMan(可以理解为Applauncher),根据入参(应用的QML文件)启动了应用。并且监听了QProcess的状态 ,用来设置对应的应用状态。
其实很多应用管理模块,启用应用的大概思路也是这样的。这种思路,在新规AppManager模块时可作为借鉴。
相关文章:
【QT】Qt Application Manager启动应用源码分析
Qt Application Manager启动应用源码分析 Qt Application Manager(以下简称QTAM)是QT推出的一款应用管理程序,可以把它简单理解成Android的LauncherSystemUI。但是,QTAM又集成了Wayland功能,并且自身实现了一套Compos…...
MyBatisPlus(十)判空查询
说明 判空查询,对应SQL语句中的 IS NULL语句,查询对应字段为 NULL 的数据。 isNull /*** 查询用户列表, 查询条件:电子邮箱为 null 。*/Testvoid isNull() {LambdaQueryWrapper<User> wrapper new LambdaQueryWrapper<…...
AIGC(生成式AI)试用 8 -- 曾经的难题
长假,远离电脑、远离手机、远离社交。 阴雨连绵,望着窗外发呆,AIGC为何物?有什么问题要问AIGC?AIGC可以代替我来发呆,还是可是为我空出时间发呆? 如果可以替代我发呆,要我何…...
文化主题公园旅游景点3d全景VR交互体验加深了他们对历史文化的认知和印象
如今,沉浸式体验被广泛应用于文旅行业,尤其是在旅游演艺活动中。在许多城市,沉浸式旅游演艺活动已成为游客“必打卡”项目之一。因其独特体验和强互动性,这类演艺活动不仅吸引了外地游客,也吸引了本地观众。 随着信息化…...
京东数据分析平台:2023年8月京东奶粉行业品牌销售排行榜
鲸参谋监测的京东平台8月份奶粉市场销售数据已出炉! 鲸参谋数据显示,8月份京东平台上奶粉的销售量将近700万件,环比增长约15%,同比则下滑约19%;销售额将近23亿元,环比增长约4%,同比则下滑约3%。…...
Java 21:虚拟线程介绍
Java 21 版本更新中最重要的功能之一就是虚拟线程 (JEP 444)。这些轻量级线程减少了编写、维护和观察高吞吐量并发应用程序所需的工作量。 正如我的许多其他文章一样,在推出新功能之前,让我们先看看 Java 21 版本更新前的现状,以便更好地了解…...
Redis-缓存穿透,缓存击穿,缓存雪崩
缓存穿透,缓存击穿,缓存雪崩 缓存穿透处理方案解决方案1 缓存空数据解决方案2 布隆过滤器 缓存击穿处理方案解决方案 1 互斥锁解决方案2 逻辑过期 缓存雪崩处理方案解决方案 1 给不同的key的过期时间设置添加一个随机值,降低同一个时段大量ke…...
如何使用Docker实现分布式Web自动化!
01、前言 顺着docker的发展,很多测试的同学也已经在测试工作上使用docker作为环境基础去进行一些自动化测试,这篇文章主要讲述在docker中使用浏览器进行自动化测试如果可以实现可视化,同时可以对浏览器进行相关的操作。 02、开篇 首先我们…...
从零开始:制作出色的产品原型图的详细教程
在设计产品的初始版本或模型时,产品原型起着非常重要的作用,可以帮助设计师和团队更好地了解产品需求和用户需求,优化和改进设计,确保设计最终满足用户的需求和期望。如果你不知道如何绘制产品原型图,绘制产品原型图的…...
美国访问学者签证如何申请加急办理?
许多中国学者梦想着前往美国深造,积累更多的学术经验和知识。然而,签证申请过程可能会变得复杂和繁琐,特别是如果你需要在紧急情况下前往美国。但别担心,本文知识人网小编将为您介绍美国访问学者签证加急办理的一些建议和步骤。 首…...
33 WEB漏洞-逻辑越权之水平垂直越权全解
目录 前言水平,垂直越权,未授权访问Pikachu-本地水平垂直越权演示(漏洞成因)墨者水平-身份认证失效漏洞实战(漏洞成因)原理越权检测-Burpsuite插件Authz安装测试(插件使用)修复防御方案 前言 越权漏洞文章分享:https://www.cnblogs.com/zhen…...
【FreeRTOS】【STM32】02 FreeRTOS 移植
基于 [野火]《FreeRTOS%20内核实现与应用开发实战—基于STM32》 正点原子《STM32F429FreeRTOS开发手册_V1.2》 准备 基础工程,例如点灯 FreeRTOS 系统源码 FreeRTOS 移植 上一章节已经说明了Free RTOS的源码文件在移植时所需要的,FreeRTOS 为我们提供…...
STM32F4X 内部FLASH使用
STM32F4X 内部FLASH使用 STM32F4X 内部FLASHSTM32F4X内部FLASH结构STM32F40X和STM32F41X内部FLASH结构STM32F42X和STM32F43X内部FLASH结构 STM32F4X内部FLASH操作例程internal_flash.hinternal_flash.cmain.c 在嵌入式开发中,经常需要实时保存一些数据。如果工程的代…...
减小windows或linux虚拟机导出ova体积大小
减小windows或linux虚拟机导出ova体积大小 删除无用的文件,比如日志或者命令,程序等;去除磁盘碎片将不用的内存空间填充为0,便于vmdk压缩。 例子: 日志文件置空: 批量置空 /sf/data/log/ 目录下的日志文…...
WPF livecharts 折线图遮挡数字问题
在WPF里使用livecharts,如果折线图或者柱状图有多个的时候,可能会出现两个数字遮挡问题,这时候要设置DataLabelsTemplate 属性。 如LineSeries设置代码如下: 第一个折线图的DataLabelsTemplate var stackPanelFactory new Fra…...
电力系统数字化升级改造之配电室无人值守
随着科技的不断进步,电力系统的数字化升级改造已成为必然趋势。其中,配电室的无人值守是其中重要的一环。 配电室是电力系统的重要组成部分,其运行状态直接影响到电力系统的稳定性和可靠性。然而,传统的配电室存在很多问题&am…...
集合Set
目录 一、去重问题 一、去重问题 题目描述: 小明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性他先用计算机生成了N个1~1000之间的随机整数(N<1000),N是用户输入的,对于其中重复的数字,只保留一个&am…...
TCP/IP(二)导论
一 知识铺垫 以下内容参照 <<电子科技大学TCPIP协议原理>>全 ① 协议和标准 一组规则: 交通规则、学生上学的学生守则等;数据通信的规则,有一个专门的名称叫作协议 protocol语义:具体描述在通信当中,每一个信息的具体含义. 二进制bit流…...
Java之UDP,TCP的详细解析
练习四:文件名重复 public class UUIDTest { public static void main(String[] args) { String str UUID.randomUUID().toString().replace("-", ""); System.out.println(str);//9f15b8c356c54f55bfcb0ee3023fce8a } } public class Client…...
【总结】kubernates crd client-java 关于自定义资源的增删改查
Java model 准备 首先使用 crd.yml 和 kubernetes CRD 自动生成 Java model 类,这是一切的前提,之前在这个地方也卡了很久。如何生成在另外一个文章中已经有所记录。 使用 crd.yml 和 kubernetes CRD 自动生成 Java model 类 CustomObjectsApi 文档学习…...
蓝牙主要知识,一文概览
蓝牙知识相关 文章目录 蓝牙知识相关1.蓝牙版本的发展简史2.低功耗BLE PHY2.1 频段**2.2 BLE调制方案—GFSK**2.3 **蓝牙 LE 传输速度、功率和接收器灵敏度**2.4 **BLE 时分双工 (TDD)**3.BT主从连接过程3.1 主设备工作模式3.1.1 积木编程控制台3.2 从设备工作模式3.2.1 蓝牙遥…...
Linux 守护进程
一 何为守护进程 守护进程( Daemon )也称为精灵进程,是运行在后台的一种特殊进程,它独立于控制终端并且周期性 地执行某种任务或等待处理某些事情的发生,主要表现为以下两个特点: 长期运行。守护进程是一…...
自动驾驶技术的基础知识
自动驾驶技术是现代汽车工业中的一项革命性发展,它正在改变着我们对交通和出行的理解。本文将介绍自动驾驶技术的基础知识,包括其概念、历史发展、分类以及关键技术要素。 1. 自动驾驶概念 自动驾驶是一种先进的交通技术,它允许汽车在没有人…...
解决:yarn 无法加载文件 “C:\Users\XXXXX\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本“ 的问题
1、问题描述: 报错的整体代码为: yarn : 无法加载文件 C:\Users\admin\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本 // 整体的报错代码为 : yarn : 无法加载文件 C:\Users\admin\AppData\Roaming\npm\yarn.ps1&…...
【JVM--StringTable字符串常量池】
文章目录 1. String 的基本特性2. 字符串拼接操作3. intern()的使用4. StringTable 的垃圾回收 1. String 的基本特性 String 声明为 final 的,不可被继承String 实现了 Serializable 接口:表示字符串是支持序列化的。String 实现了 Comparable 接口&am…...
Large Language Models Meet Knowledge Graphs to Answer Factoid Questions
本文是LLM系列文章,针对《Large Language Models Meet Knowledge Graphs to Answer Factoid Questions》的翻译。 大型语言模型与知识图谱相遇,回答虚假问题 摘要1 引言2 相关工作3 提出的方法4 实验设计5 结果与讨论6 结论 摘要 最近,有研…...
blender 之视频渲染(以三维重建path为例)
blender 之视频渲染(以三维重建path为例) 1.新建轨迹路径2.设置相机,使其按照path运动3.将相机视角对准物体4.修改帧率5.设置输出路径6.设置输出格式7.渲染 1.新建轨迹路径 新建轨迹 选中新建的BezierCycle,按住S,拖…...
leetcode做题笔记166. 分数到小数
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。 如果小数部分为循环小数,则将循环的部分括在括号内。 如果存在多个答案,只需返回 任意一个 。 对于所有给定的输入,保证 …...
Android Studio新建项目缓慢解决方案
关于Android Studio2022新建项目时下载依赖慢的解决方案 起因解决方案gradle下载慢解决方案kotlin依赖下载慢解决方案 结尾 起因 新建Android Studio项目时,常会因为网络问题导致部分依赖下载缓慢,其中gradle和kotlin最拖慢进度。 解决方案 gradle下载…...
AmdU (5-azidomethyl-2‘-deoxyuridine)的反应原理|59090-48-1
产品简介:叠氮甲基dU(AmdU)是一种核苷类化合物,它含有叠氮基团,这种结构特点使其在细胞学和生物学领域得到了广泛应用。与胸腺嘧啶核苷相似的结构,使得叠氮甲基dU(AmdU)能够被细胞聚…...
合肥网红/网络营销优化推广
1. Spinlock 只适用于短暂的等待,因为没有进程切换所以对于短暂等待他的效率会比较高。 但是对于长时间等待,由于它的CPU占用是100% 等的越长越不合算。 Semaphore的实现机制可以描述如下:CPU首先会检测信号量是否可获取,如果无法…...
网站推广怎么做流量大/百度搜索关键词怎么刷上去
数据中台基础 数据中台严格意义上来说不是一种产品或技术,而是一套可持续 “让企业的数据用起来” 的机制,一种战略选择和组织形式,是依据企业特有的业务模式和组织机构,通过成熟的产品和实施方法论支撑,构建的一套持…...
网站建设商业计划书范文/网络推广的主要内容
SpringMVC注解 Controller:在当前类使用,相当于 implements Controller。 RequestMapping(""):浏览器访问的路径,可以加在类上或者方法上。 PathVariable:放在方法行参上,用于RestFul风格。 GetMapping("Param/p1/{a}/{b}&q…...
wordpress用户中心商城/实时热搜榜
很多人在搜索下载过PDF转换器的小伙伴都会有一个灵魂拷问:难道就没有免费还没页数限制的PDF转Word的工具吗?小编经过不断的对比和试用,找到以下两款好用免费的工具,相信总有一个你能用上。一、PDF转换器相信了解PDF这种文档格式设…...
苹果电脑做网站好用吗/郑州网站建设价格
本文实例讲述了php实现mysqli批量执行多条语句的方法。分享给大家供大家参考,具体如下:可以一次性的执行多个操作或取回多个结果集。实例:$mysqli new mysqli("localhost", "root", "111111", "test&quo…...
做兼职的翻译网站吗/学网络营销有用吗
爱普生墨仓式打印机(L616X,L617X,L619X,L416X,L316X) 首先检查是否卡纸,卡纸就慢慢的拽出来,下面的内容都是建立在打印机能正常完成整个打印的过程,存在的问题:打印出来的是纯白纸,没有任何显示…...