<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
QxxxFactory类:插件生产者
在Qt的插件加载机制的概述中,我已经提到过,一个Q<pluginType>Factory 类往往对应于某一类别、或某种特定功能的插件。
在Qt中,为了区分不同类别、不同功能的插件,应该为每一类插件设置一个独特的 IID 值,这个IID值通常
是一个长字符串。属于同一类的插件应该具有相同的IDD值。比如,所有平台类QPA插件,包括LinuxFB插件(QLinuxFbIntegration)、
XCB插件(QXcbIntegration)等,他们的IDD都应是 org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2 ,
而所有的输入法类插件,如Qt的ibus插件、fcitx插件等,他们的IDD都应该是 org.qt-project.Qt.QPlatformInputContextFactoryInterface。
另外我提到过,Qt还会为每个Q<pluginType>Factory 类,即每一个类别的插件,绑定一个 QFactoryLoader 对象,
这个QFactoryLoader 对象中也记录了这一类插件的IID的值,专门负责加载这一类别的插件。
接下来,就分别研究一下这两个类。先看 QxxxFactory。这里为了具体一点,我们拿QPlatformInputContextFactory类来讲,其他的QxxxFactory
类也都是类似的。
- class Q_GUI_EXPORT QPlatformInputContextFactory
- {
- public:
- static QStringList keys();
- static QPlatformInputContext *create(const QString &key);
- static QPlatformInputContext *create();
- };
其中,keys() 方法用于获得所有同类插件的关键字。对于QPlatformInputContextFactory类,它的keys()方法自然就是获得所有输入法类插件,比如ibus\fcitx等插件的关键字。
- QStringList QPlatformInputContextFactory::keys()
- {
- #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
- return loader()->keyMap().values();
- #else
- return QStringList();
- #endif
- }
获取关键字的这段代码很短,但我们马上就有个疑问,代码中出现的 loader() 是哪儿来的?它的返回值是什么?
我最初读到这段代码时也一致纠结于此,因为哪里都找不到这个 loader() 的定义,后来才注意到,在这个源文件的
开头,有一句
- #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
- Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
- (QPlatformInputContextFactoryInterface_iid, QLatin1String("/platforminputcontexts"), Qt::CaseInsensitive))
- #endif
这才有点眉目,而且在Qt Assistanct中也找到了 Q_GLOBAL_STATIC_WITH_ARGS 这个宏的说明,这个宏专门用于定义全局的静态变量,
Q_GLOBAL_STATIC_WITH_ARGS( Type, VariableName, Arguments)
其中Type是要定义的静态全局变量的类型,VariableName是变量的名字,Arguments是构造参数。使用这个宏,可以定义一个变量为
VariableName、类型为 QGlobalStatic 的对象。而 QGlobalStatic 类重载了括号运算符 '()',通过VariableName()可以得到一个
Type类型的指针,他指向的就是我们所需要的那个静态全局变量。Arguments就是在构造这个Type类型的静态全局对象时传给构造函数
的参数。
因此上面那句代码,就是定义了一个 QFactoryLoader 类的静态全局对象,这样在之后的代码中,就可以通过 loader() 访问这个对象了。
loader()->keyMap().values() 这一行,就把这个 QFactoryLoader 对象对应的所有插件的关键字获取到组成一个列表,本文后面会介绍QFactoryLoader类。
QPlatformInputContextFactory 有两个 create 方法,其中一个是指定了关键字,另一个则自己搜索关键字。
- QPlatformInputContext *QPlatformInputContextFactory::create(const QString& key)
- {
-
- QStringList paramList = key.split(QLatin1Char(':'));
- const QString platform = paramList.takeFirst().toLower();
-
- #if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
- if (QPlatformInputContext *ret = qLoadPlugin1<QPlatformInputContext, QPlatformInputContextPlugin>(loader(), platform, paramList))
- return ret;
- #endif
- return 0;
- }
-
-
-
- QPlatformInputContext *QPlatformInputContextFactory::create()
- {
-
- QPlatformInputContext *ic = 0;
-
-
- QString icString = QString::fromLatin1(qgetenv("QT_IM_MODULE"));
-
- if (icString == QLatin1String("none"))
- return 0;
-
- ic = create(icString);
- if (ic && ic->isValid())
- return ic;
-
- delete ic;
- ic = 0;
-
-
- QStringList k = keys();
- for (int i = 0; i < k.size(); ++i) {
- if (k.at(i) == icString)
- continue;
- ic = create(k.at(i));
- if (ic && ic->isValid())
- return ic;
- delete ic;
- ic = 0;
- }
-
- return 0;
- }
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
QFactoryLoader 类:插件加载者
如前所说,一个 QFactoryLoader 对象用于加载某一类别的插件。QFactoryLoader 类中维护了一个
插件/库列表(列表中的元素都是QLibraryPrivate类型),这个列表中的插件/库都是属于同一类别的插件,
他们的 IID 都是一样的(一个插件/库的IID值存储在其QLibraryPrivate对象的元信息metaData中)。
除了这个插件/库列表,QFactoryLoader 类中还维护了一个从关键字到插件/库的映射表,通过这个映射表
可以快速的通过关键字来找到对应的库。
这里我们先来看下QFactoryLoaderPrivate的定义:
- class QFactoryLoaderPrivate : public QObjectPrivate
- {
- Q_DECLARE_PUBLIC(QFactoryLoader)
- public:
- QFactoryLoaderPrivate(){}
- ~QFactoryLoaderPrivate();
-
-
-
-
-
-
-
-
-
- mutable QMutex mutex;
- QByteArray iid;
- QList<QLibraryPrivate*> libraryList;
- QMap<QString,QLibraryPrivate*> keyMap;
- QString suffix;
- Qt::CaseSensitivity cs;
- QStringList loadedPaths;
-
- void unloadPath(const QString &path);
- };
QFactoryLoaderPrivate类的iid、suffix和cs成员,实在QFactoryLoader类的构造函数中初始化的,下面马上会看到。
QFactoryLoader类的定义如下。
- class Q_CORE_EXPORT QFactoryLoader : public QObject
- {
- Q_OBJECT
- Q_DECLARE_PRIVATE(QFactoryLoader)
-
- public:
- explicit QFactoryLoader(const char *iid,
- const QString &suffix = QString(),
- Qt::CaseSensitivity = Qt::CaseSensitive);
-
-
-
-
- ~QFactoryLoader();
-
- QList<QJsonObject> metaData() const;
- QObject *instance(int index) const;
-
- #if defined(Q_OS_UNIX) && !defined (Q_OS_MAC)
- QLibraryPrivate *library(const QString &key) const;
- #endif
-
- QMultiMap<int, QString> keyMap() const;
-
-
-
-
- int indexOf(const QString &needle) const;
-
- void update();
-
- static void refreshAll();
- };
通过上面的注释我们已经知道,库列表(d->libraryList) 和 关键字到库的映射表(d->keyMap)都是在构造函数中通过调用update()生成的,
这两个表几乎就是这个类的核心,下面我们就看看这两个表是如何生成的。