文章目录

一、前言

​插件开发总结–插件的创建及使用​​一文中,展示了在Qt中如何使用Qt Low-API插件实例。但是这却满足不了大型应用程序的实际场景,没有扩展性。而插件间的通信、加载卸载(释放内存)、插件元数据、插件生命周期、插件依赖等问题,便是我们要做的。在QT内部,高级 API 有 PluginManager 负责做这些事,但是低级 API 就需要自己写插件管理器来帮助我们解决这些问题。

想象一台 windows 系统的电脑,包含了主机、显示屏、键鼠等部件。假如我们拔掉键盘,电脑不会出错,只是缺失了键盘的功能,因此键盘就可以看做是一个插件。与此同时一台完整的电脑不仅包含了键鼠,还有耳机、音响、光驱、显卡等部件,这些部件其实都可以看成插件。对 windows 来说,这些“插件”都有一个管理者,即为设备管理器。设备管理器负责添加和删除电脑所有的硬件和驱动,因此可以将设备管理器理解为插件管理器。最后一点电脑系统都有自己的内核,一个 windows 系统从启动到关机都是内核在响应,而内核就可以看做加载插件的主程序,仿佛:“一旦你插上,我就能用你来打游戏”。

Qt插件开发总结--插件管理器_#include


二、项目结构

Qt插件开发总结--插件管理器_#define_02


上文中的程序,主程序和插件程序是分开的,不便管理,本文中采取父子工程结构,如上图,创建流程如下:

1、创建子目录项目

Qt插件开发总结--插件管理器_json_03


2、创建子项目

Qt插件开发总结--插件管理器_#include_04


Main、pluginA、pluginB都是子项目

PluginInterface.h

#ifndef
#define

#include <QObject>
#include <QJsonObject>

struct PluginMetaData
{
QString from; //消息来源
QString dest; //消息去向
QString msg; //消息

QObject* object = nullptr;
QJsonObject info = QJsonObject();
};
Q_DECLARE_METATYPE(PluginMetaData); //确保类型可以通过信号槽传递

//定义接口
class PluginInterface
{
public:
virtual ~PluginInterface(){}
virtual void recMsgFromManager(PluginMetaData) = 0; //接收来自插件管理器的消息
virtual void sendMsgToManager(PluginMetaData) = 0; //发生消息到插件管理器
};

//一定是唯一的标识符
#define

QT_BEGIN_NAMESPACE
Q_DECLARE_INTERFACE(PluginInterface,PluginInterface_iid)
QT_END_NAMESPACE

#endif// PLUGININTERFACE_H

widget.h

#ifndef
#define

#include <QWidget>
#include <QMessageBox>
#include <QDebug>

#include "PluginInterface.h"
#include "PluginManager.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
Widget(QWidget *parent = nullptr);
~Widget();

private:
Ui::Widget *ui;
};
#endif// WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);

PluginManager::instance()->loadAllPlugins();
//qDebug()<<"allPluginsName: "<<PluginManager::instance()->allPluginsName();

QPluginLoader *loader1 = PluginManager::instance()->getPlugin("pluginA");
if(loader1) {
PluginInterface *pluginA = dynamic_cast<PluginInterface*>(loader1->instance());
if(pluginA) {
PluginMetaData m;
m.dest = "pluginB";
m.from = "pluginA";
m.msg = "插件A发给插件B的消息";
pluginA->sendMsgToManager(m);
}
}

QPluginLoader *loader2 = PluginManager::instance()->getPlugin("pluginB");
if(loader2) {
PluginInterface *pluginB = dynamic_cast<PluginInterface*>(loader2->instance());
if(pluginB) {
PluginMetaData m;
m.dest = "pluginA";
m.from = "pluginB";
m.msg = "插件B发给插件A的消息";
pluginB->sendMsgToManager(m);
}
}

}

Widget::~Widget()
{
delete ui;
}

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}

pluginA.pro

QT += widgets

TEMPLATE = lib #表明这个makefile是一个lib的makefile
CONFIG += plugin #应用程序是一个插件

TARGET = pluginA #插件名称
DESTDIR = ../plugins # 输出目录

HEADERS += \
pluginA.h

SOURCES += \
pluginA.cpp

DISTFILES += \
pluginA.json

pluginA.h

#ifndef
#define

#include <QObject>
#include <QtPlugin>
#include <QDebug>
#include "../Main/PluginInterface.h"

class PluginA : public QObject,public PluginInterface
{
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "pluginA.json")
public:
explicit PluginA(QObject *parent = nullptr);
void show_pluginA();

void recMsgFromManager(PluginMetaData metaData);

signals:
void sendMsgToManager(PluginMetaData);
};

#endif// PLUGINA_H

pluginA.cpp

#include "pluginA.h"

PluginA::PluginA(QObject *parent) : QObject(parent)
{

}

void PluginA::show_pluginA()
{
qDebug()<<"这是插件A";
}

void PluginA::recMsgFromManager(PluginMetaData metaData)
{
qDebug()<<"插件A接收到消息:"<<metaData.msg;
}

pluginB.pro

QT += widgets

TEMPLATE = lib #表明这个makefile是一个lib的makefile
CONFIG += plugin #应用程序是一个插件

TARGET = pluginB #插件名称
DESTDIR = ../plugins # 输出目录

HEADERS += \
pluginB.h

SOURCES += \
pluginB.cpp

DISTFILES += \
pluginB.json

pluginB.h

#ifndef
#define

#include <QObject>
#include <QtPlugin>
#include <QDebug>
#include "../Main/PluginInterface.h"

class PluginB : public QObject,public PluginInterface
{
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID PluginInterface_iid FILE "pluginB.json")
public:
explicit PluginB(QObject *parent = nullptr);
void show_pluginB();

void recMsgFromManager(PluginMetaData metaData);

signals:
void sendMsgToManager(PluginMetaData);
};


#endif// PLUGINB_H

pluginB.cpp

#include "pluginB.h"

PluginB::PluginB(QObject *parent) : QObject(parent)
{

}

void PluginB::show_pluginB()
{
qDebug()<<"这是插件B";
}

void PluginB::recMsgFromManager(PluginMetaData metaData)
{
qDebug()<<"插件B接收到消息:"<<metaData.msg;
}

三、创建插件管理器文件

在主程序Main项目中创建PluginManager类

#ifndef
#define

#include "PluginInterface.h"
#include <QObject>
#include <QPluginLoader>
#include <QVariant>

class PluginManager : public QObject
{
Q_OBJECT
public:
explicit PluginManager(QObject *parent = nullptr);
~PluginManager();

static PluginManager *instance(){
if(m_instance==nullptr)
m_instance=new PluginManager();
return m_instance;
}

//扫描JSON文件中的插件元数据
void scanMetaData(const QString &filepath);

//加载所有插件
void loadAllPlugins();

//加载其中某个插件
void loadPlugin(const QString &filepath);

//卸载所有插件
void unloadAllPlugins();

//卸载某个插件
void unloadPlugin(const QString &filepath);

//获取所有插件名称
QList<QVariant> allPluginsName();

//获取所有插件
QList<QPluginLoader *> allPlugins();

//获取某个插件名称
QVariant getPluginName(QPluginLoader *loader);

//根据名称获得插件
QPluginLoader* getPlugin(const QString &name);

public slots:
void recMsgFromPlugin(PluginMetaData);


private:
static PluginManager *m_instance;

class PluginsManagerPrivate;
PluginsManagerPrivate *managerPrivate;
};

#endif// PLUGINMANAGER_H
#include "PluginManager.h"
#include <QDir>
#include <QCoreApplication>
#include <QJsonArray>
#include <QDebug>

PluginManager* PluginManager::m_instance=nullptr;

class PluginManager::PluginsManagerPrivate
{
public:
PluginsManagerPrivate()
{
m_names.clear();
m_versions.clear();
m_dependencies.clear();
m_loaders.clear();
}
~PluginsManagerPrivate(){}

QHash<QString, QVariant> m_names; //插件路径--插件名称
QHash<QString, QVariant> m_versions; //插件路径--插件版本
QHash<QString, QVariantList> m_dependencies; //插件路径--插件额外依赖的其他插件
QHash<QString, QPluginLoader *> m_loaders; //插件路径--QPluginLoader实例

bool check(const QString &filepath) //插件依赖检测
{
bool status = true;

foreach (QVariant item, m_dependencies.value(filepath)) {
QVariantMap map = item.toMap();
// 依赖的插件名称、版本、路径
QVariant name = map.value("name");
QVariant version = map.value("version");
QString path = m_names.key(name);

/********** 检测插件是否依赖于其他插件 **********/
// 先检测插件名称
if (!m_names.values().contains(name)) {
qDebug() << Q_FUNC_INFO << " Missing dependency:" << name.toString() << "for plugin" << path;
status = false;
continue;
}

// 再检测插件版本
if (m_versions.value(path) != version) {
qDebug() << Q_FUNC_INFO << " Version mismatch:" << name.toString() << "version"
<< m_versions.value(m_names.key(name)).toString() << "but" << version.toString() << "required for plugin" << path;
status = false;
continue;
}

// 然后,检测被依赖的插件是否还依赖于另外的插件
if (!check(path)) {
qDebug() << Q_FUNC_INFO << "Corrupted dependency:" << name.toString() << "for plugin" << path;
status = false;
continue;
}
}

return status;
}

};


PluginManager::PluginManager(QObject *parent) : QObject(parent)
{
managerPrivate = new PluginsManagerPrivate;
}
PluginManager::~PluginManager()
{
delete managerPrivate;
}

void PluginManager::loadAllPlugins()
{
QDir pluginsDir(qApp->applicationDirPath()); //pluginsDir: "../build-xxx-debug/debug"
if(pluginsDir.dirName().toLower() == "debug" ||
pluginsDir.dirName().toLower() == "release") {
pluginsDir.cdUp(); //pluginsDir: "../build-xxx-debug"
pluginsDir.cdUp(); //pluginsDir: "../"
}
pluginsDir.cd("plugins");

QFileInfoList pluginsInfo = pluginsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);

//初始化插件中的元数据
for(QFileInfo fileinfo : pluginsInfo){
//qDebug()<<"loadAllPlugins:"<<fileinfo.absoluteFilePath();
scanMetaData(fileinfo.absoluteFilePath());
}

//加载插件
for(QFileInfo fileinfo : pluginsInfo)
loadPlugin(fileinfo.absoluteFilePath());
}

void PluginManager::scanMetaData(const QString &filepath)
{
//判断是否为库(后缀有效性)
if(!QLibrary::isLibrary(filepath))
return ;

//获取元数据
QPluginLoader *loader = new QPluginLoader(filepath);

//qDebug()<<loader->metaData().keys();
QJsonObject json = loader->metaData().value("MetaData").toObject();
// for(int i=0; i<json.keys().size(); ++i) {
// qDebug()<<json.keys().at(i)<< " : "<<json.value(json.keys().at(i));
// }

managerPrivate->m_names.insert(filepath, json.value("name").toVariant());
managerPrivate->m_versions.insert(filepath, json.value("version").toVariant());
managerPrivate->m_dependencies.insert(filepath, json.value("dependencies").toArray().toVariantList());

delete loader;
loader = nullptr;
}

void PluginManager::loadPlugin(const QString &filepath)
{
if(!QLibrary::isLibrary(filepath))
return;

//检测依赖
if(!managerPrivate->check(filepath))
return;

//加载插件
QPluginLoader *loader = new QPluginLoader(filepath);
if(loader->load()) {
PluginInterface *plugin = qobject_cast<PluginInterface *>(loader->instance());
if(plugin) {
managerPrivate->m_loaders.insert(filepath, loader);
connect(loader->instance(),SIGNAL(sendMsgToManager(PluginMetaData)),
this,SLOT(recMsgFromPlugin(PluginMetaData)));
}else {
delete loader;
loader = nullptr;
}
}else{
qDebug()<<"loadPlugin:"<<filepath<<loader->errorString();
}
}

void PluginManager::unloadAllPlugins()
{
for(QString filepath : managerPrivate->m_loaders.keys())
unloadPlugin(filepath);
}

void PluginManager::unloadPlugin(const QString &filepath)
{
QPluginLoader *loader = managerPrivate->m_loaders.value(filepath);
//卸载插件,并从内部数据结构中移除
if(loader->unload()) {
managerPrivate->m_loaders.remove(filepath);
delete loader;
loader = nullptr;
}
}

QList<QPluginLoader *> PluginManager::allPlugins()
{
return managerPrivate->m_loaders.values();
}

QList<QVariant> PluginManager::allPluginsName()
{
return managerPrivate->m_names.values();
}

QVariant PluginManager::getPluginName(QPluginLoader *loader)
{
if(loader)
return managerPrivate->m_names.value(managerPrivate->m_loaders.key(loader));
else
return "";
}

QPluginLoader *PluginManager::getPlugin(const QString &name)
{
return managerPrivate->m_loaders.value(managerPrivate->m_names.key(name));
}

void PluginManager::recMsgFromPlugin(PluginMetaData metaData)
{
auto loader = getPlugin(metaData.dest); //目标插件
if(loader) {
auto interface = qobject_cast<PluginInterface*>(loader->instance());
if(interface) {
interface->recMsgFromManager(metaData); //转发给对应的插件
}
}
}

四、插件管理器的使用

直接编译运行,就会生成插件在plugins文件夹中,无须移动,程序加载插件会直接去此文件夹中加载插件

Qt插件开发总结--插件管理器_#include_05


Qt插件开发总结--插件管理器_qt_06

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);

PluginManager::instance()->loadAllPlugins(); //加载所有插件
//qDebug()<<"allPluginsName: "<<PluginManager::instance()->allPluginsName();

QPluginLoader *loader1 = PluginManager::instance()->getPlugin("pluginA");
if(loader1) {
PluginInterface *pluginA = dynamic_cast<PluginInterface*>(loader1->instance());
if(pluginA) {
PluginMetaData m;
m.dest = "pluginB";
m.from = "pluginA";
m.msg = "插件A发给插件B的消息";
pluginA->sendMsgToManager(m);
}
}

QPluginLoader *loader2 = PluginManager::instance()->getPlugin("pluginB");
if(loader2) {
PluginInterface *pluginB = dynamic_cast<PluginInterface*>(loader2->instance());
if(pluginB) {
PluginMetaData m;
m.dest = "pluginA";
m.from = "pluginB";
m.msg = "插件B发给插件A的消息";
pluginB->sendMsgToManager(m);
}
}

}

Qt插件开发总结--插件管理器_json_07