<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
class Q_GUI_EXPORT QPlatformInputContextFactory
{
public:
static QStringList keys(); // 只有几个静态的成员函数
static QPlatformInputContext *create(const QString &key);
static QPlatformInputContext *create();
};
QStringList QPlatformInputContextFactory::keys()
{
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
return loader()->keyMap().values();
#else
return QStringList();
#endif
}
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, // <--- loader就在这里定义
(QPlatformInputContextFactoryInterface_iid, QLatin1String("/platforminputcontexts"), Qt::CaseInsensitive))
#endif
QPlatformInputContext *QPlatformInputContextFactory::create(const QString& key)
{
// 指定了关键字 key
QStringList paramList = key.split(QLatin1Char(':')); // 将关键字按分隔符分离成一个或多个参数,分隔符是冒号
const QString platform = paramList.takeFirst().toLower(); // 获取key中分离的第一个参数,作为加载插件时用的关键字
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
if (QPlatformInputContext *ret = qLoadPlugin1(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;
// 如果环境变量中找不到合适的关键字,则从 keys() 返回的关键字列表中一个一个试
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;
}
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
class QFactoryLoaderPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QFactoryLoader)
public:
QFactoryLoaderPrivate(){} // 构造函数什么也不做
~QFactoryLoaderPrivate(); // 析构函数中卸载并释放插件/库列表中所有的库
/* QFactoryLoaderPrivate::~QFactoryLoaderPrivate()
{
for (int i = 0; i < libraryList.count(); ++i) {
QLibraryPrivate *library = libraryList.at(i);
library->unload();
library->release();
}
} */
mutable QMutex mutex;
QByteArray iid; // 当前对象对应的插件集的 IID
QList libraryList; // 插件/库 列表
QMap keyMap; // 插件/库 到 关键字的映射表
QString suffix; // 当前对象对应的插件集中所有插件/库对应的库文件(.so文件)路径的后缀(最低一级目录的名字)
Qt::CaseSensitivity cs; // 匹配关键字的策略,是精确匹配还是粗略匹配,一般都选粗略匹配
QStringList loadedPaths; // 所有已经加载过的库路径
void unloadPath(const QString &path);
};
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);
// 构造函数,先设置插件集的 IID、插件路径后缀和关键字匹配策略,
// 再调用update()生成插件列表和插件到关键字的映射表,并将当前的
// QFactoryLoader对象添加到全局的loader列表qt_factory_loaders中 。
~QFactoryLoader(); // 析构,将当前的QFactoryLoader对象从全局的loader列表qt_factory_loaders中移除
QList metaData() const; // 返回d->libraryList(库列表)中每个库的元信息组成的列表(列表的还加上了静态库的元信息)
QObject *instance(int index) const; // 返回d->libraryList(库列表)第index个插件的实例
#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC)
QLibraryPrivate *library(const QString &key) const; // 返回映射表中关键字key对应的库
#endif
QMultiMap keyMap() const; // 返回d->libraryList(库列表)中所有库的关键字映射表:每个库有
// 一个int型索引(就是该库在库列表中的索引)和若干个QString
// 类型的关键字,这个函数的返回值是QMultiMap类型的一个“一对多”的映射表,一个
// 库(索引)对应若干个关键字。
int indexOf(const QString &needle) const; // 返回关键字needle对应的库在库列表中的索引
void update(); // 主要功能就是更新 库列表(d->libraryList) 和 关键字到库的映射表(d->keyMap), 一般在应用程序的库加载路径发生变化时才需调用
static void refreshAll(); // 这是一个静态函数,用于将全局loader列表(qt_factory_loaders)中的所有QFactoryLoader对象都update() 一下
};
void QFactoryLoader::update()
{
#ifdef QT_SHARED
Q_D(QFactoryLoader);
QStringList paths = QCoreApplication::libraryPaths(); // 获取应用程序的库加载路径
// 第一层循环, 开始遍历所有的库路径
for (int i = 0; i < paths.count(); ++i) {
const QString &pluginDir = paths.at(i); // 获取第 i 个库路径
// Already loaded, skip it... 如果当前库路径已经记录在了 d->loadedPaths 中,则跳过这个库路径。
if (d->loadedPaths.contains(pluginDir))
continue;
d->loadedPaths << pluginDir; // 将当前库路径添加到 d->loadedPaths 中。这样可以防止重复加载一个库路径
QString path = pluginDir + d->suffix; // 这个库路径加上当前QFactoryLoader对象对应的插件集的路径后缀,得到插件集的路径path(如果存在)
if (qt_debug_component())
qDebug() << "QFactoryLoader::QFactoryLoader() checking directory path" << path << "...";
if (!QDir(path).exists(QLatin1String("."))) // 检测插件集路径path是否存在,不存在则跳过当前的库路径
continue;
// 如果插件集路径path存在,接着往下运行
QStringList plugins = QDir(path).entryList(QDir::Files); // 列出当前插件集路径path中的所有文件名,存入plugins列表中
QLibraryPrivate *library = 0;
#ifdef Q_OS_MAC
// Loading both the debug and release version of the cocoa plugins causes the objective-c runtime
// to print "duplicate class definitions" warnings. Detect if QFactoryLoader is about to load both,
// skip one of them (below).
//
// ### FIXME find a proper solution
//
const bool isLoadingDebugAndReleaseCocoa = plugins.contains(QStringLiteral("libqcocoa_debug.dylib"))
&& plugins.contains(QStringLiteral("libqcocoa.dylib"));
#endif
// 第二层循环,开始遍历插件集路径path中的所有文件
for (int j = 0; j < plugins.count(); ++j) {
QString fileName = QDir::cleanPath(path + QLatin1Char('/') + plugins.at(j)); // 获取第 j 个文件的文件名
#ifdef Q_OS_MAC
if (isLoadingDebugAndReleaseCocoa) {
#ifdef QT_DEBUG
if (fileName.contains(QStringLiteral("libqcocoa.dylib")))
continue; // Skip release plugin in debug mode
#else
if (fileName.contains(QStringLiteral("libqcocoa_debug.dylib")))
continue; // Skip debug plugin in release mode
#endif
}
#endif
if (qt_debug_component()) {
qDebug() << "QFactoryLoader::QFactoryLoader() looking at" << fileName;
}
// 尝试根据文件名fileName创建库library (库用QLibraryPrivate类的对象表示)
// 疑问: 如果插件集路径中包含有非库的普通文件(比如文本文件、图片),也要为他们生成一个 library ? 所以不应该在插件集路径中放非库的文件?
library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath());
if (!library->isPlugin()) {
// 判断该库(library)是不是插件,如果不是插件,则跳过当前的库
if (qt_debug_component()) {
qDebug() << library->errorString;
qDebug() << " not a plugin";
}
library->release();// 释放该库
continue;
}
// 如果该库(library)是插件,则继续往下运行
QStringList keys;
bool metaDataOk = false;
QString iid = library->metaData.value(QLatin1String("IID")).toString();
if (iid == QLatin1String(d->iid.constData(), d->iid.size())) {
// 如果插件library的IID与当前QFactoryLoader对象对应的插件集的IID相同,则设置metaDataOk标志,并
// 读取该插件library的元信息中的 MetaData:Keys 字段,这个字段是个JSON数组类型,
// 存储了若干字符串类型的关键字,将这些关键字天价到字符串列表 keys 中。
QJsonObject object = library->metaData.value(QLatin1String("MetaData")).toObject();
metaDataOk = true; // 设置metaDataOk标志
QJsonArray k = object.value(QLatin1String("Keys")).toArray();
for (int i = 0; i < k.size(); ++i)
keys += d->cs ? k.at(i).toString() : k.at(i).toString().toLower();
}
if (qt_debug_component())
qDebug() << "Got keys from plugin meta data" << keys;
if (!metaDataOk) { // 如果metaDataOk标志未被设置,说明IID不匹配,跳过library这个插件
library->release();
continue;
}
int keyUsageCount = 0; // 映射计数,对映射到 library 上的关键字计数
// 第三层小循环,遍历library元信息中Keys字段存储的所有关键字
// 这个循环建立了 库到关键字的映射表(的一部分)
for (int k = 0; k < keys.count(); ++k) {
// first come first serve, unless the first
// library was built with a future Qt version,
// whereas the new one has a Qt version that fits
// better
const QString &key = keys.at(k); // 获取第k个关键字key
// 如果映射表已经映射过 key 了,则获取其之前映射的那个库previous及其所用的Qt版本号prev_qt_version,
// 跟当前库library的Qt版本号qt_version进行比较,如果prev_qt_version高于当前的QT版本号并且qt_version
// 不大于当前的QT版本号,则将关键字 key 重新映射到 library 。
// 如果映射表中还未映射过关键字key,则直接将其映射到 library
QLibraryPrivate *previous = d->keyMap.value(key);
int prev_qt_version = 0;
if (previous) {
prev_qt_version = (int)previous->metaData.value(QLatin1String("version")).toDouble();
}
int qt_version = (int)library->metaData.value(QLatin1String("version")).toDouble();
if (!previous || (prev_qt_version > QT_VERSION && qt_version <= QT_VERSION)) {
// 如果映射表中还未映射过关键字key(!previous), 或者之前映射到key的库的Qt版本号不合适时,
// 都讲 key 映射到 library 。
d->keyMap[key] = library;
++keyUsageCount; // 映射计数加1
}
}
// 如果映射计数大于0或者库library的元信息中的MetaData:Keys 字段为空(没有关键字),则将库library添加到库列表d->libraryList 中
if (keyUsageCount || keys.isEmpty())
d->libraryList += library; // 这里建立了 库列表(的一部分)
else
library->release();
}
}
#else
Q_D(QFactoryLoader);
if (qt_debug_component()) {
qDebug() << "QFactoryLoader::QFactoryLoader() ignoring" << d->iid
<< "since plugins are disabled in static builds";
}
#endif
}