乐于分享
好东西不私藏

第五章 插件:插件开发指南

本文最后更新于2026-01-02,某些文章具有时效性,若有错误或已失效,请在下方留言或联系老夜

第五章 插件:插件开发指南

目录

  1. 概述
  2. SDK核心组件
  3. 插件开发步骤
  4. 关键实现要点
  5. 资源管理
  6. 插件加载与测试
  7. 代码优化建议
  8. 总结

1. 概述

ScreenCast支持通过插件系统扩展对不同设备类型的支持。本文档详细介绍了如何利用SDK为目标设备平台开发对应的插件,使ScreenCast能够与新的设备类型进行交互。

插件系统基于Qt的插件框架实现,每个插件都是一个动态链接库(DLL/SO/dylib),实现特定的接口以支持新设备类型。

2. SDK核心组件

2.1 插件接口 (IDevicePlugin)

IDevicePlugin是所有设备插件必须实现的核心接口,定义了插件的基本信息和行为:

classIDevicePlugin
{

public:
virtual ~IDevicePlugin() = default;

// 插件基本信息
virtual QString pluginName()const0;
virtual QString pluginVersion()const0;
virtual QIcon pluginIcon()const0;
virtual QString description()const0;
virtual QString author()const0;

// 设备代理创建
virtual DeviceProxy* createDeviceProxy(QObject* parent = nullptr)0;
virtual DeviceType deviceType()const0;

// 驱动相关
virtualboolcheckDriverAvailable()const0;
virtual QString getDriverName()const0;
virtual QString getDriverPath()const0;
};

2.2 设备代理接口 (DeviceProxy)

DeviceProxy是设备代理的抽象接口,定义了与设备交互的所有方法:

classDeviceProxy :public QObject
{
    Q_OBJECT
public:
// 设备列表和信息查询
virtual QVector<DeviceInfo> listDevices()0;
virtual QString deviceModel(const QString& serial)0;
virtual QString deviceName(const QString& serial)0;
virtual QSize deviceResolution(const QString& serial)0;
virtual DeviceType deviceType()const0;
virtualboolqueryDeviceInfo(const QString& serial, DeviceInfo& info)0;

// 镜像服务器相关
virtualboolsetupMirrorServer(const QString& serial, int forwardPort)0;
virtualboolstartMirrorServer(const QString& serial)0;
virtualboolstopMirrorServer(const QString& serial)0;
virtual QString getMirrorServerIp(const QString& serial)0;

// 设备控制
virtualboolisScreenOn(const QString& serial)0;
virtualintdeviceRotation(const QString& serial)0;
virtualboolsendEvent(const DeviceInfo& dev, DeviceEvent eventType)0;
virtualboolsendTouchEvent(const DeviceInfo& dev, QPoint pos)0;
virtualboolsendTextEvent(const DeviceInfo& dev, const QString& text)0;
virtualboolsendSwipeEvent(const DeviceInfo& dev, QPoint start, QPoint end, int duration = 300)0;
virtualboolscreenshot(const QString& serial, QByteArray& imageData)0;
virtualboolsupportEvent(DeviceEvent eventType)const0;

// 信号
    signals:
voidserverStarted(const QString& serial);
voidserverStopped(const QString& serial);
};

2.3 设备信息结构 (DeviceInfo)

DeviceInfo结构体包含设备的基本信息:

enumclassDeviceType
{

    Unknown,
    Android,
    OHOS,
    iOS,
    Windows,
    Linux,
    MacOS,
};

enumclassDeviceEvent
{

    INVALID = 0,
    BACK, HOME, MENU, WAKEUP, SLEEP, ROTATE, ROTATE_LOCK,
    UNLOCK, SHUTDOWN, REBOOT, TOUCH, KEY, TEXT,
    VOLUME_UP, VOLUME_DOWN, MUTE, POWER, VOLUME_MUTE, SWIPE
};

structDeviceInfo
{

    QString serial;  // 设备序列号
    QString model;   // 设备型号
    QString name;    // 设备名称
    DeviceType type; // 设备类型
int forwardPort; // 端口转发端口
int width;       // 屏幕宽度
int height;      // 屏幕高度
int rotation;    // 设备旋转角度
};

3. 插件开发步骤

3.1 创建插件项目

  1. 创建新的Qt库项目

    • 使用Qt Creator创建一个新的库项目(选择”Library”类型)
    • 选择”Qt Plugin”作为库类型
    • 设置项目名称(例如NewDevicePlugin
  2. 配置项目文件

   QT += core widgets gui

   TARGET = $$qtLibraryTarget(newdeviceplugin)
   TEMPLATE = lib
   CONFIG += plugin

   # 指定插件输出目录
   DESTDIR = $$PWD/../output/plugins

   # 添加SDK头文件路径
   INCLUDEPATH += $$PWD/../../sdk/include

   # 添加源文件
   SOURCES += \
       NewDevicePlugin.cpp \
       NewDevice.cpp

   HEADERS += \
       NewDevicePlugin.h \
       NewDevice.h

   # 添加资源文件
   RESOURCES += newdevice.qrc

3.2 实现插件接口

  1. 创建插件类
#pragma once

#include"NewDevice.h"
#include"plugin/IDevicePlugin.h"

classNewDevicePlugin :public QObject, public IDevicePlugin
   {
Q_OBJECT
Q_INTERFACES(IDevicePlugin)
Q_PLUGIN_METADATA(IID "IDevicePlugin")

public:
NewDevicePlugin(QObject* parent = nullptr)
;
       ~NewDevicePlugin() override = default;

QString pluginName()constoverride;
QString pluginVersion()constoverride;
QIcon pluginIcon()constoverride;

DeviceProxy* createDeviceProxy(QObject* parent = nullptr)override;

DeviceType deviceType()constoverride;

boolcheckDriverAvailable()constoverride;
QString description()constoverride;
QString author()constoverride;
QString getDriverName()constoverride;
QString getDriverPath()constoverride;
   };
  1. 实现插件方法
#include"NewDevicePlugin.h"

#include<QCoreApplication>
#include<QDir>
#include<QProcessEnvironment>
#include<QStandardPaths>

   NewDevicePlugin::NewDevicePlugin(QObject* parent) : QObject(parent) {}

QString NewDevicePlugin::pluginName()const
{
return"New Device Type";
   }

QString NewDevicePlugin::pluginVersion()const
{
return"1.0.0";
   }

QIcon NewDevicePlugin::pluginIcon()const
{
return QIcon(":/newdevice/icon");
   }

DeviceProxy* NewDevicePlugin::createDeviceProxy(QObject* parent)
{
returnnew NewDevice(parent);
   }

DeviceType NewDevicePlugin::deviceType()const
{
return DeviceType::NewDevice; // 假设已在DeviceType中添加
   }

boolNewDevicePlugin::checkDriverAvailable()const
{
       QString driverPath = getDriverPath();
return !driverPath.isEmpty();
   }

QString NewDevicePlugin::description()const
{
return"New device type support plugin";
   }

QString NewDevicePlugin::author()const
{
return"Developer Name";
   }

QString NewDevicePlugin::getDriverName()const
{
return"newdevdriver";
   }

QString NewDevicePlugin::getDriverPath()const
{
// 1. 检查环境变量
       QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
       QString path = env.value("NEWDEV_HOME");
if (!path.isEmpty())
       {
           QString driverPath = QDir(path).filePath("bin/newdevdriver");
if (QFile::exists(driverPath))
           {
return driverPath;
           }
       }

// 2. 检查PATH环境变量中的驱动
       QString driverPath = QStandardPaths::findExecutable("newdevdriver");
if (!driverPath.isEmpty())
       {
return driverPath;
       }

// 3. 检查应用程序目录
       QString appDir = QCoreApplication::applicationDirPath();
       driverPath = QDir(appDir).filePath("newdevdriver");
if (QFile::exists(driverPath))
       {
return driverPath;
       }

return QString();
   }

3.3 实现设备代理

  1. 创建设备代理类
#pragma once

#include<QMutex>
#include<device/DeviceInfo.h>
#include<device/DeviceProxy.h>

classNewDevice :public DeviceProxy
   {
       Q_OBJECT

public:
explicitNewDevice(QObject* parent = nullptr);

// DeviceProxy接口实现
QVector<DeviceInfo> listDevices()override;
QString deviceModel(const QString& serial)override;
QString deviceName(const QString& serial)override;
QSize deviceResolution(const QString& serial)override;
DeviceType deviceType()constoverride;
boolqueryDeviceInfo(const QString& serial, DeviceInfo& info)override;

// 镜像服务器相关
boolsetupMirrorServer(const QString& serial, int forwardPort)override;
boolstartMirrorServer(const QString& serial)override;
boolstopMirrorServer(const QString& serial)override;
QString getMirrorServerIp(const QString& serial)override;

// 屏幕相关
boolisScreenOn(const QString& serial)override;
intdeviceRotation(const QString& serial)override;

// 事件发送相关
boolsendEvent(const DeviceInfo& dev, DeviceEvent eventType)override;
boolsendTouchEvent(const DeviceInfo& dev, QPoint pos)override;
boolsendTextEvent(const DeviceInfo& dev, const QString& text)override;
boolsendSwipeEvent(const DeviceInfo& dev, QPoint start, QPoint end, int duration = 300)override;

// 截图相关
boolscreenshot(const QString& serial, QByteArray& imageData)override;

// 功能支持检查
boolsupportEvent(DeviceEvent eventType)constoverride;

private:
// 私有辅助方法
boolpushResourceToDevice(const QString& serial, const QString& sourcePath, const QString& destPath);

private:
       QMutex m_mutex; // 线程安全锁
// 其他私有成员变量
   };
  1. 实现设备代理方法: 设备代理的实现需要根据具体设备的通信协议来完成。以下是一些关键方法的实现示例:
#include"NewDevice.h"

#include"utils/Shell.h"

#include<QDebug>
#include<QRegularExpression>

   NewDevice::NewDevice(QObject* parent) : DeviceProxy(parent)
   {
       qDebug() << "NewDevice::NewDevice";
   }

QVector<DeviceInfo> NewDevice::listDevices()
{
       QVector<DeviceInfo> devices;
       QString output;

// 使用设备专用命令列出设备
if (!Shell::executeCommandQuiet("newdevdriver", {"list"}, &output))
       {
return devices;
       }

// 解析输出并构建设备信息列表
       QStringList lines = output.split('\n', Qt::SkipEmptyParts);
for (const QString& line : lines)
       {
// 根据设备命令输出格式解析
           QString serial = parseSerialFromOutput(line); // 自定义解析方法
if (!serial.isEmpty())
           {
               DeviceInfo info;
               info.type = DeviceType::NewDevice;
               info.serial = serial;
               devices.append(info);
           }
       }

return devices;
   }

boolNewDevice::sendTouchEvent(const DeviceInfo& dev, QPoint pos)
{
// 实现触摸事件发送
       QString command = QString("tap %1 %2").arg(pos.x()).arg(pos.y());
return Shell::executeCommandQuiet("newdevdriver", {"-s", dev.serial, "input", command});
   }

boolNewDevice::supportEvent(DeviceEvent eventType)const
{
// 根据设备支持的事件类型返回
switch (eventType)
       {
case DeviceEvent::TOUCH:
case DeviceEvent::HOME:
case DeviceEvent::BACK:
returntrue;
// 其他事件类型...
default:
returnfalse;
       }
   }

4. 关键实现要点

4.1 设备通信

设备插件通常需要通过特定的命令行工具或API与设备通信。ScreenCast SDK提供了Shell类来简化命令执行:

// 使用Shell类执行命令
QString output;
bool success = Shell::executeCommandQuiet("driver_cmd", {"arg1""arg2"}, &output);

4.2 镜像服务器部署

为了实现屏幕镜像功能,插件需要:

  1. 将镜像服务器程序推送到设备
  2. 设置端口转发
  3. 启动/停止镜像服务器
boolNewDevice::setupMirrorServer(const QString& serial, int forwardPort)
{
// 将服务器程序推送到设备
if (!pushResourceToDevice(serial, ":/newdevice/server""/data/local/tmp/mirror_server"))
    {
        qWarning() << "Failed to push mirror server to device";
returnfalse;
    }

// 设置端口转发
return Shell::executeCommandQuiet("newdevdriver", {"-s", serial, "forward"":" + QString::number(forwardPort), "tcp:7890"});
}

boolNewDevice::startMirrorServer(const QString& serial)
{
// 启动镜像服务器
bool success = Shell::executeCommandQuiet("newdevdriver", {"-s", serial, "shell""/data/local/tmp/mirror_server start"});
if (success)
    {
emit serverStarted(serial);
    }
return success;
}

4.3 线程安全

由于设备操作可能在多线程环境中执行,需要确保线程安全:

boolNewDevice::listDevices()
{
QMutexLocker locker(&m_mutex)// 加锁保护
// 实现设备列表获取
// ...
}

5. 资源管理

5.1 图标资源

为插件添加图标资源:

  1. 创建newdevice.qrc文件:
<!DOCTYPE RCC>
<RCCversion="1.0">
<qresource>
<filealias="icon">res/icon.png</file>
<filealias="server">res/mirror_server</file>
</qresource>
</RCC>
  1. 在插件中使用图标:
QIcon NewDevicePlugin::pluginIcon()const
{
return QIcon(":/newdevice/icon");
   }

5.2 镜像服务器资源

镜像服务器程序需要作为资源文件包含在插件中,并在运行时推送到设备:

boolNewDevice::pushResourceToDevice(const QString& serial, const QString& sourcePath, const QString& destPath)
{
// 实现将资源推送到设备的逻辑
// 可以使用临时文件中转
    QTemporaryFile tempFile;
if (tempFile.open())
    {
QFile resourceFile(sourcePath);
if (resourceFile.open(QIODevice::ReadOnly))
        {
            tempFile.write(resourceFile.readAll());
            tempFile.close();

// 推送临时文件到设备
return Shell::executeCommandQuiet("newdevdriver", {"-s", serial, "push", tempFile.fileName(), destPath});
        }
    }
returnfalse;
}

6. 插件加载与测试

6.1 构建和部署插件

  1. 构建插件生成动态库文件
  2. 将生成的插件文件复制到ScreenCast应用的plugins目录
  3. 启动ScreenCast应用,系统会自动加载插件

6.2 调试技巧

  1. 设置环境变量
   QT_DEBUG_PLUGINS=1

这将输出插件加载的详细信息,帮助排查加载问题。

  1. 检查依赖:确保插件依赖的所有库都可用。

  2. 日志输出:在关键位置添加调试日志,帮助跟踪问题。

7. 代码优化建议

7.1 错误处理

boolNewDevice::sendEvent(const DeviceInfo& dev, DeviceEvent eventType)
{
// 增强的错误处理
if (!supportEvent(eventType))
    {
        qWarning() << "Event type not supported:" << static_cast<int>(eventType);
returnfalse;
    }

// 执行命令并捕获错误
    QString command = eventToString(eventType);
    QString output;
    QString error;

if (!Shell::executeCommand("newdevdriver", {"-s", dev.serial, "input", command}, &output, &error))
    {
        qWarning() << "Failed to send event:" << error;
returnfalse;
    }

returntrue;
}

7.2 超时控制

为设备操作添加超时控制,避免长时间阻塞:

boolNewDevice::executeWithTimeout(const QStringList& arguments, QString* output, int timeoutMs = 5000)
{
// 实现带超时的命令执行
    QProcess process;
    process.start("newdevdriver", arguments);

if (!process.waitForStarted())
    {
returnfalse;
    }

if (!process.waitForFinished(timeoutMs))
    {
        process.kill();
        process.waitForFinished();
        qWarning() << "Command timed out";
returnfalse;
    }

if (output)
    {
        *output = process.readAllStandardOutput();
    }

return process.exitCode() == 0;
}

7.3 缓存机制

对频繁查询的设备信息实施缓存,提高性能:

QString NewDevice::deviceName(const QString& serial)
{
// 使用缓存避免重复查询
static QHash<QString, QString> nameCache;

if (nameCache.contains(serial))
    {
return nameCache[serial];
    }

// 执行实际查询
    QString name = actualDeviceNameQuery(serial);
    nameCache[serial] = name;

return name;
}

8. 总结

通过本文档,我们详细介绍了如何使用ScreenCast SDK开发设备插件。插件开发的核心步骤包括:

  1. 创建Qt插件项目
  2. 实现IDevicePlugin接口
  3. 实现DeviceProxy接口
  4. 处理设备通信和镜像服务器部署
  5. 添加资源文件和图标

开发插件时,需要特别注意线程安全、错误处理和资源管理。通过遵循本文档中的指南和最佳实践,开发者可以高效地为ScreenCast添加新设备类型的支持,扩展应用的兼容性和功能。

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 第五章 插件:插件开发指南
×
订阅图标按钮