【QT】Qt Application Manager启动应用源码分析

news2024/12/23 6:12:22

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_OBJECT
    Q_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
	// 第一次启动时,这里为NotRunning
    if (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);
			// 激活App
            emitActivated(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()) {
			// 默认使用ProcessContainer
            containerId = qSL("process");
        } else {
            // check config file
            for (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) {
				// 创建Container
                container = 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);
        else
            runtime->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);
            else
                runtime->deleteLater(); // ~Runtime() will clean app->nonAliased()->m_runtime

            return successfullyStarted;
        };

        auto tryStartInContainer = [container, doStartInContainer]() -> bool {
            if (container->isReady()) {
                // Since the container is already ready, start the app immediately
                return 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
# else
                connect(container, &AbstractContainer::ready, doStartInContainer);
#endif
                return 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::NotRunning
    switch (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);
	
	// 获取Icon
    QString 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 possible
        env.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 crashed
        m_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模块时可作为借鉴。

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

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

相关文章

MyBatis-plus快速代码生成,使用MybatisX插件

概述 MyBatis提供逆向工程&#xff0c;可以快速生成代码。 而MyBatis-plus也提供了一个代码生成器&#xff0c;它需要执行一些代码来实现。 而MybatisX 是一款基于 IDEA 的快速开发插件&#xff0c;为效率而生&#xff0c;你不需要写代码&#xff0c;直接界面化操作&#xf…

Ultralytics(YoloV8)开发环境配置,训练,模型转换,部署全流程测试记录

关键词&#xff1a;windows docker tensorRT Ultralytics YoloV8 配置开发环境的方法&#xff1a; 1.Windows的虚拟机上配置&#xff1a; Python3.10 使用Ultralytics 可以得到pt onnx&#xff0c;但无法转为engine&#xff0c;找不到GPU&#xff0c;手动转也不行&#xff0…

【Java 进阶篇】JDBC 登录案例详解

在本文中&#xff0c;我们将通过一个简单的 JDBC 登录案例来详细介绍如何使用 Java 数据库连接&#xff08;JDBC&#xff09;来连接数据库、进行用户身份验证等操作。这个案例将适用于数据库初学者&#xff0c;我们将从头开始构建一个简单的登录系统。 什么是 JDBC&#xff1f…

剑指offer——JZ18 删除链表的节点 解题思路与具体代码【C++】

一、题目描述与要求 删除链表的节点_牛客题霸_牛客网 (nowcoder.com) 题目描述 给定单向链表的头指针和一个要删除的节点的值&#xff0c;定义一个函数删除该节点。返回删除后的链表的头节点。 1.此题对比原题有改动 2.题目保证链表中节点的值互不相同 3.该题只会输出返回…

视频讲解|基于DistFlow潮流的配电网故障重构代码

目录 1 主要内容 2 视频链接 1 主要内容 该视频为基于DistFlow潮流的配电网故障重构代码讲解内容&#xff0c;对应的资源下载链接为基于DistFlow潮流的配电网故障重构(输入任意线路)&#xff0c;对该程序进行了详尽的讲解&#xff0c;基本做到句句分析和讲解&#xff08;讲解…

反序列化漏洞

原理 反序列化是对象变成可以传输的字符串。 PHP反序列化的时候&#xff0c;基本都是围绕着serialize()&#xff0c;unserialize()这两个函数 serialize() 和 unserialize() 在 PHP内部没有漏洞的&#xff0c;产生反序列化漏洞是因为应用程序在处理对象、魔术函数以及序列化…

《C和指针》笔记33:指针数组

除了创建整型数组一样&#xff0c;也可以声明指针数组。 int *api[10];为了弄清这个复杂的声明&#xff0c;我们假定它是一个表达式&#xff0c;并对它进行求值。下标引用的优先级高于间接访问&#xff0c;所以在这个表达式中&#xff0c;首先执行下标引用。因此&#xff0c;a…

[软件工具]opencv-svm快速训练助手教程解决opencv C++ SVM模型训练与分类实现任务支持C# python调用

opencv中已经提供了svm算法可以对图像实现多分类&#xff0c;使用svm算法对图像分类的任务多用于场景简单且对时间有要求的场景&#xff0c;因为opencv的svm训练一般只需要很短时间就可以完成训练任务。但是目前网上没有一个工具很好解决训练问题&#xff0c;大部分需要自己编程…

分布式锁:四种方案解决商品超卖的方案

一 分布式锁 1.1 分布式锁的作用 在多线程高并发场景下&#xff0c;为了保证资源的线程安全问题&#xff0c;jdk为我们提供了synchronized关键字和ReentrantLock可重入锁&#xff0c;但是它们只能保证一个工程内的线程安全。在分布式集群、微服务、云原生横行的当下&#xff…

仿牛客论坛项目 笔记

文章目录 环境配置bean是什么最终成品功能数据库与缓存一致性整个web系统后端的结构spring mvc相关controller常见的代码写法mybatis相关常识测试、调试相关计网相关component相关注解spring全家桶族谱spring衍生框架 run之后发生了什么什么是spring&#xff0c;spring和bean的…

剑指offer——JZ22 链表中倒数最后k个结点 解题思路与具体代码【C++】

一、题目描述与要求 链表中倒数最后k个结点_牛客题霸_牛客网 (nowcoder.com) 题目描述 输入一个长度为 n 的链表&#xff0c;设链表中的元素的值为 ai &#xff0c;返回该链表中倒数第k个节点。 如果该链表长度小于k&#xff0c;请返回一个长度为 0 的链表。 数据范围&…

计算机网络(第8版)第一章概述笔记

6 性能指标 带宽&#xff1a; 在单位时间内从网络中的某一点到另一点所能通过的“最高数据率”。 7 分层结构、协议、接口、服务 1、实体&#xff1a;第n层的活动元素称为n层实体。同一层的实体叫对等实体。 2、协议&#xff1a;为进行网络中的对等实体数据交换而建立的规则、…

js判断数据类型、toString和valueOf区别,类型转换、不同类型间的运算、判断相等

目录 判断数据类型 运算符 typeof&#xff1a;判断 基本数据类型 typeof nullObject 类型标签均为000 实例 instanceof 构造函数&#xff1a;判断原型链&#xff0c;和isPrototypeOf 方法 构造函数.prototype.isPrototypeOf(实例) &#xff1a;判断原型链 (数据).const…

[React源码解析] React的设计理念和源码架构 (一)

任务分割异步执行让出执法权 文章目录 1.React的设计理念1.1 Fiber1.2 Scheduler1.3 Lane1.4 代数效应 2.React的源码架构2.1 大概图示2.2 jsx2.3 Fiber双缓存2.4 scheduler2.5 Lane模型2.6 reconciler2.7 renderer2.8 concurrent 3.React源码调试 1.React的设计理念 Fiber: 即…

究竟是什么样的讲解数组算法的博客让我写了三小时???

版本说明 当前版本号[20231004]。 版本修改说明20231004初版 目录 文章目录 版本说明目录二. 基础数据结构2.1 数组1) 概述2) 动态数组1&#xff09;插入addlast 方法测试: addlast 方法 add 方法测试&#xff1a;add方法 addlast 方法与 add 方法合并版get 方法测试&#x…

MyCat实现分库分表技术

目录 一、分库分表 1.1介绍 1.1.1问题分析 1.1.2拆分策略 1.1.3垂直拆分 1.1.3.1垂直分库 1.1.3.2垂直分表 1.1.4水平拆分 1.1.4.1水平分库 1.1.4.2水平分表 1.1.5实现技术 二、MyCat概述 2.1介绍 2.2下载 2.3安装 2.4目录介绍 2.5概念介绍 三、MyCat入门 3.…

某道翻译逆向

文章目录 前文crypto模块分析完整代码结尾 前文 本文章中所有内容仅供学习交流&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; crypto模块 Crypto是加密的简称&#…

10月4日作业

server #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//实例化一个服务器server new QTcpServer(this);connect(server, &QTcpServer::newConnection, …

CANoe.Diva生成测试用例

Diva目录 一、CANoe.Diva打开CDD文件二、导入CDD文件三、ECU Information四、时间参数设置五、选择是否测试功能寻址六、勾选需要测试服务项七、生成测试用例 一、CANoe.Diva打开CDD文件 CANoe.Diva可以通过导入cdd或odx文件&#xff0c;自动生成全面的测试用例。再在CANoe中导…

Android学习之路(20) 进程间通信

IPC IPC为 (Inter-Process Communication) 缩写&#xff0c;称为进程间通信或跨进程通信&#xff0c;指两个进程间进行数据交换的过程。安卓中主要采用 Binder 进行进程间通信&#xff0c;当然也支持其他 IPC 方式&#xff0c;如&#xff1a;管道&#xff0c;Socket&#xff0…