QtCreator源码分析 -3.插件管理系统

39 篇文章 0 订阅

首先,我们先看看QT的插件系统。

QT的插件模型类似于在COM本质论前面部分内容里描述的模型(不过还没有去看具体的源码,实现机制是否一样还不确定)。动态链接库通过继承一个简单接口的纯虚类,在需要的时候动态载入,然后通过纯虚类的接口函数进行进一步的访问。从而为动态链接库提供一个统一的发现方式。

 

在QT的插件系统中,提供给对外使用的主要有IPlugin、PluginManager、PluginSpec这三个类,PluginView、PluginErrorView、PluginDetailsView三个辅助类。我们主要研究三个主要类IPlugin、PluginManager、PluginSpec。这三个类的具体实现由IPluginPrivate、PluginManagerPrivate、PluginSpecPrivate三个类实现,目的是对外屏蔽实现的具体细节。

 

1、IPlugin,IPluginPrivate 插件接口类

IPlugin类为插件的接口类(纯虚类),其作用是为动态链接库导出类提供统一的发现接口,从而达到插件的目的。

image

每个插件都需要至少提供initialize(const QStringList &arguments, QString *errorString)和extensionsInitialized()两个函数,为插件类提供初始化功能。

其中addObject、addAutoReleasedObject、removeObject三个函数为插件类的实例化提供计数功能。

2、PluginSpec、PluginSpecPrivate 插件说明类

插件说明类以插件的说明信息提供服务,包括名字、版本、位置、依赖关系,参数、状态等等。

image

 

3、PluginManager、PluginManagerPrivate 插件管理类

插件管理类提供对插件的管理功能,为我们这次主要研究的类。

image 

PluginManager类对外提供的函数主要分为三类:对象池操作、插件操作、和对于参数的操作。

对于对象池的操作有addObject()添加对象、removeObject()移除对象、allObjects()获取所有对象、getObjects()获取某个类的所有对象、getObject()获取某个类的第一个对象。在getObjects()和getObject()中使用了Aggregation类的查询操作。并且在对象添加和移除后引发objectAdded和aboutToRemoveObject信号。

对于插件的操作主要有loadPlugins()载入插件、setPluginPaths()、pluginPaths()设置和获取插件路径、plugins()获取插件配置列表、setFileExtension()、fileExtension设置插件配置文件的类型。关于插件的操作是需要主要分析的对象,我按照在主程序中的操作依序分析相关的函数。

3.1、void setFileExtension(const QString &extension)         设置插件配置文件类型

void PluginManager::setFileExtension(const QString &extension)
{
    d->extension = extension;
}
仅仅对细节实现类的extension插件配置文件类型属性进行了赋值。
3.2、void setPluginPaths(const QStringList &paths)   设置插件路径列表
void PluginManager::setPluginPaths(const QStringList &paths)
{
    d->setPluginPaths(paths);
}
void PluginManagerPrivate::setPluginPaths(const QStringList &paths)
{
    pluginPaths = paths;
    readPluginPaths();
}
void PluginManagerPrivate::readPluginPaths()
{
    qDeleteAll(pluginSpecs);
    pluginSpecs.clear();

    QStringList specFiles;
    QStringList searchPaths = pluginPaths;
    while (!searchPaths.isEmpty()) {   //找出所有的插件配置文件
        const QDir dir(searchPaths.takeFirst());
       const QFileInfoList files = dir.entryInfoList(QStringList() << QString("*.%1").arg(extension), QDir::Files); //后缀名为extension定义的文件
        foreach (const QFileInfo &file, files)
            specFiles << file.absoluteFilePath();
       const QFileInfoList dirs = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot);
       foreach (const QFileInfo &subdir, dirs)
            searchPaths << subdir.absoluteFilePath(); //添加当前文件夹的子文件夹
    }
    foreach (const QString &specFile, specFiles) {
       PluginSpec *spec = new PluginSpec;
       spec->d->read(specFile);  //读取配置文件
        pluginSpecs.append(spec);	  //添加到配置列表
    }
    resolveDependencies();  //解析依赖关系
    // ensure deterministic plugin load order by sorting
    qSort(pluginSpecs.begin(), pluginSpecs.end(), lessThanByPluginName); //按字母从小到大排序
    emit q->pluginsChanged();
}

可以看出在设置插件路径的函数中,首先找出了所有的插件的配置文件,再添加到配置列表中,然后再解析其依赖关系。

在解析其依赖关系的函数中bool PluginSpecPrivate::resolveDependencies(const QList<PluginSpec *> &specs),会检查每个所依赖的所有的项目,如果有项目不存在,则会报错。

3.3、pluginManager.parseOptions(arguments, appOptions, &foundAppOptions, &errorMessage)解析启动参数

bool PluginManager::parseOptions(const QStringList &args,
    const QMap<QString, bool> &appOptions,
    QMap<QString, QString> *foundAppOptions,
    QString *errorString)
{
    OptionsParser options(args, appOptions, foundAppOptions, errorString, d);
    return options.parse();
}

OptionsParser 类解析具体的参数,并将对于各个插件的参数填入插件配置类的信息中。

3.4、pluginManager.loadPlugins() 载入插件

void PluginManagerPrivate::loadPlugins()
{
    QList<PluginSpec *> queue = loadQueue();
    foreach (PluginSpec *spec, queue) {
        loadPlugin(spec, PluginSpec::Loaded); //载入插件
    }
    foreach (PluginSpec *spec, queue) {
        loadPlugin(spec, PluginSpec::Initialized); //初始化插件
    }
    QListIterator<PluginSpec *> it(queue);
    it.toBack();
    while (it.hasPrevious()) {
        loadPlugin(it.previous(), PluginSpec::Running);//运行插件
    }
    emit q->pluginsChanged();
}
void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destState)
{
    if (spec->hasError())
        return;
    if (destState == PluginSpec::Running) {
        spec->d->initializeExtensions();  //运行插件
        return;
    } else if (destState == PluginSpec::Deleted) {
        spec->d->kill();
        return;
    }
    //检查插件的依赖项
    foreach (PluginSpec *depSpec, spec->dependencySpecs()) {
        if (depSpec->state() != destState) {
            spec->d->hasError = true;
            spec->d->errorString =
                PluginManager::tr("Cannot load plugin because dependency failed to load: %1(%2)/nReason: %3")
                    .arg(depSpec->name()).arg(depSpec->version()).arg(depSpec->errorString());
            return;
        }
    }
    if (destState == PluginSpec::Loaded)
        spec->d->loadLibrary();
    else if (destState == PluginSpec::Initialized)
        spec->d->initializePlugin();
    else if (destState == PluginSpec::Stopped)
        spec->d->stop();
}
对插件执行了加载、初始化、运行三个操作。对这三个操作具体分析。
加载:
bool PluginSpecPrivate::loadLibrary()
{
    //一些检查和插件的动态链接库的名字的形成
    ....

    PluginLoader loader(libName);
    if (!loader.load()) {
        ....   
    }
    //插件载入得到插件接口类
    IPlugin *pluginObject = qobject_cast<IPlugin*>(loader.instance());
   if (!pluginObject) {
        ....    
   }
   state = PluginSpec::Loaded;
   plugin = pluginObject;
   plugin->d->pluginSpec = q; //设置插件的配置文档
   return true;
}
初始化:
bool PluginSpecPrivate::initializePlugin()
{
    ....//一些检查
    if (!plugin->initialize(arguments, &err)) {//带参数的初始化
        ...
   }
   state = PluginSpec::Initialized;
   return true;
}
其中插件接口类的initialize函数为纯虚函数,由每个具体的接口实现。
运行:
bool PluginSpecPrivate::initializeExtensions()
{
    ...//一些检查
    if (!plugin) {
	...
    }
    plugin->extensionsInitialized(); //运行
    state = PluginSpec::Running;
    return true;
}

其中插件接口类的extensionsInitialized函数为纯虚函数,由每个具体的接口实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值