Qt5 plug-in mechanism (2) -- QxxxFactory class, QFactoryLoader class, chromereloader plug-in

Source: Internet
Author: User
Tags keyword list

Qt5 plug-in mechanism (2) -- QxxxFactory class, QFactoryLoader class, chromereloader plug-in

<<<

QxxxFactory class: plug-in producer

In the overview of the Qt plug-in loading mechanism, I have mentioned that a Q <pluginType> Factory class often corresponds to a class or a plug-in with a specific function.
In Qt, a unique IID value should be set for each plug-in to distinguish between different categories and functions.
Is a long string. Plug-ins of the same class should have the same IDD value. For example, all platform-class QPA plug-ins, including LinuxFB plug-ins (QLinuxFbIntegration ),
XCB plug-ins (QXcbIntegration) and so on, they should all be org. qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2,
All input method class plug-ins, such as Qt ibus plug-ins, fcitx plug-ins, etc., their IDD should be org. qt-project.Qt.QPlatformInputContextFactoryInterface.

In addition, I mentioned that Qt will bind a QFactoryLoader object for each Q <pluginType> Factory class, that is, the plug-in of each category,
This QFactoryLoader object also records the IID value of this type of plug-in, specifically responsible for loading this type of plug-in.

Next, we will study these two classes respectively. First look at QxxxFactory. For specific purpose, we use the QPlatformInputContextFactory class to consider other QxxxFactory
Classes are similar.


Class Q_GUI_EXPORT QPlatformInputContextFactory {public: static QStringList keys (); // only a few static member functions static QPlatformInputContext * create (const QString & key); static QPlatformInputContext * create ();};



The keys () method is used to obtain the keywords of all similar plug-ins. For the QPlatformInputContextFactory class, its keys () method is naturally used to obtain the keywords of all input method class plug-ins, such as ibus \ fcitx and Other plug-ins.
QStringList QPlatformInputContextFactory::keys(){#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)    return loader()->keyMap().values();#else    return QStringList();#endif}

The code for retrieving keywords is very short, but we will immediately have a question: where does the loader () in the code come from? What is its return value?
When I first read this code, I also struggled with it because I couldn't find the definition of loader (). Later I noticed that
First, there is a sentence
# If! Defined (QT_NO_LIBRARY )&&! Defined (QT_NO_SETTINGS) Evaluate (QFactoryLoader, loader, // <--- loader is defined here (struct, QLatin1String ("/platforminputcontexts"), Qt: CaseInsensitive) # endif



The macro Q_GLOBAL_STATIC_WITH_ARGS is also found in Qt Assistanct. This macro is used to define global static variables,
Q_GLOBAL_STATIC_WITH_ARGS (Type, VariableName, Arguments)
Here, Type is the Type of the static global variable to be defined, VariableName is the name of the variable, and Arguments is the construction parameter. Using this macro, you can define a variable
VariableName: an object of the QGlobalStatic type. The QGlobalStatic class reloads the bracket operator '()' and uses VariableName () to obtain
Type pointer, which points to the static global variable we need. Arguments is passed to the constructor when constructing a static Global Object of the Type.
.
Therefore, the above Code defines a static Global Object of the QFactoryLoader class, so that the object can be accessed through loader () in later code.
For the line loader ()-> keyMap (). values (), the keywords of all plug-ins corresponding to the QFactoryLoader object are obtained to form a list. This article will introduce the QFactoryLoader class later.

QPlatformInputContextFactory has two create methods. One is to specify the keyword, and the other is to search for the keyword.

QPlatformInputContext * QPlatformInputContextFactory: create (const QString & key) {// specifies the key QStringList paramList = key. split (QLatin1Char (':'); // separate keywords into one or more parameters by separator. The separator is colon const QString platform = paramList. takeFirst (). toLower (); // obtain the first parameter separated from the key as the keyword used to load the plug-in # if! Defined (QT_NO_LIBRARY )&&! Defined (QT_NO_SETTINGS) if (QPlatformInputContext * ret = qLoadPlugin1 <QPlatformInputContext, attributes> (loader (), platform, paramList) return ret; # endif return 0;} QPlatformInputContext * attributes :: create () {// if no keyword is specified, search for the keyword by yourself. QPlatformInputContext * ic = 0; // first search for the keyword QString icString = QString: fromLatin1 (qgetenv ("QT_IM_MODULE") from the environment variable ")); if (icString = QLatin1String ("none") return 0; ic = create (icString); if (ic & ic-> isValid () return ic; delete ic; ic = 0; // if no suitable keyword is found in the environment variable, test QStringList k = keys () one by one from the keyword list returned by 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 class: plug-in loader
As mentioned above, a QFactoryLoader object is used to load a type of plug-ins. The QFactoryLoader class maintains
Plug-in/library list (all elements in the list are of the QLibraryPrivate type). All plug-ins/libraries in this list belong to the same category,
Their IIDS are the same (the IID values of a plug-in/library are stored in metaData of the QLibraryPrivate object ).
In addition to the plug-in/library list, the QFactoryLoader class also maintains a ing table from the keyword to the plug-in/library.
You can quickly find the corresponding library through keywords.

Here we will first look at the definition of QFactoryLoaderPrivate:

Class QFactoryLoaderPrivate: public QObjectPrivate {Q_DECLARE_PUBLIC (QFactoryLoader) public: QFactoryLoaderPrivate () {}// constructor does nothing ~ QFactoryLoaderPrivate (); // uninstall and release the INS/all libraries in the library list/* 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 of the plug-in set corresponding to the current object <QLibraryPrivate *> libraryList; // plug-in/library list QMap <QString, QLibraryPrivate *> keyMap; // INS/ ing tables from libraries to keywords QString suffix; // library files corresponding to all plug-ins/libraries of the current object (. so file) path suffix (the name of the lowest level directory) Qt: casesensiti1_cs; // match the keyword policy, whether it is exact match or rough match, generally, select roughly match QStringList loadedPaths; // all the loaded library paths void unloadPath (const QString & path );};

The iid, suffix, and cs members of the QFactoryLoaderPrivate class are initialized in the constructor of the QFactoryLoader class. We will see them immediately below.


The QFactoryLoader class is defined as follows.

Class required QFactoryLoader: public QObject {Q_OBJECT Q_DECLARE_PRIVATE (QFactoryLoader) public: explicit QFactoryLoader (const char * iid, const QString & suffix = QString (), Qt: timeout = Qt :: caseSensitive); // constructor. First, set the IID, extension path suffix, and keyword matching policy of the plug-in set, // call update () generate a ing table between the ins and keywords, and add the current // QFactoryLoader object to the global loader list qt_factory_loaders. ~ QFactoryLoader (); // destructor: removes the current QFactoryLoader object from the global loader list qt_factory_loaders. <QJsonObject> metaData () const; // returns the list of metadata of each database in the d-> libraryList (library list) (The list also includes metadata of the static library) QObject * instance (int index) const; // returns the instance of the index plug-in d-> libraryList (library list) # if defined (Q_ OS _UNIX )&&! Defined (Q_ OS _MAC) QLibraryPrivate * library (const QString & key) const; // returns the database corresponding to the key in the ing table # endif QMultiMap <int, QString> keyMap () const; // return to the keyword ing table of all databases in the d-> libraryList (database list): Each database has a // int index (that is, the index of the database in the Database List) and several QString // type keywords. the return value of this function is a "one-to-many" ing table of the QMultiMap type, and a // database (INDEX) corresponds to several keywords. Int indexOf (const QString & needle) const; // returns the index void update () of the library corresponding to the keyword needle in the library list (); // The main function is to update the library list (d-> libraryList) and the ing table (d-> keyMap) from the keyword to the library ), generally, you need to call static void refreshAll () when the library loading path of the application changes. // This is a static function used to list the global loaders (qt_factory_loaders) all QFactoryLoader objects in update };


Through the above comments, we know that the library list (d-> libraryList) and the ing table (d-> keyMap) from the keywords to the library are all in the constructor by calling update () generated,
These two tables are almost the core of this class. Let's take a look at how these two tables are generated.

Void QFactoryLoader: update () {# ifdef QT_SHARED Q_D (QFactoryLoader); QStringList paths = QCoreApplication: libraryPaths (); // obtain the library loading path of the application // the first layer of the loop, start traversing all the library paths for (int I = 0; I <paths. count (); ++ I) {const QString & pluginDir = paths. at (I); // obtain the path of the I library // Already loaded, skip it... if the current library path has been recorded in d-> loadedPaths, skip this library path. If (d-> loadedPaths. contains (pluginDir) continue; d-> loadedPaths <pluginDir; // Add the current library path to d-> loadedPaths. This prevents repeated loading of a library path QString path = pluginDir + d-> suffix; // This library path is appended with the path suffix of the plug-in set corresponding to the current QFactoryLoader object, obtain the path of the plug-in SET (if any) if (qt_debug_component () qDebug () <"QFactoryLoader: QFactoryLoader () checking directory path "<path <"... "; if (! QDir (path ). exists (QLatin1String (". ") // check whether the path of the plug-in set exists. If the path does not exist, skip the current database path continue. // if the path of the plug-in set exists, then run QStringList plugins = QDir (path ). entryList (QDir: Files); // lists all file names in the path of the current plug-in set and stores them in the plugins list. 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 // Layer 2 loop, start to traverse all the files in the path of the plug-in set path for (int j = 0; j <plugins. count (); ++ j) {QString fileName = QDir: cleanPath (path + QLatin1Cha R ('/') + plugins. at (j); // obtain the file name of file j # ifdef Q_ OS _MAC if (isLoadingDebugAndReleaseCocoa) {# ifdef QT_DEBUG if (fileName. contains (QStringLiteral ("libqcocoa. dylib ") continue; // Skip release plugin debug mode # else if (fileName. contains (QStringLiteral ("plugin") continue; // Skip debug plugin in release mode # endif} # endif if (qt_debug_component () {qDebug () <"QFactoryLoader:: QFactoryLoader () looking at "<fileName;} // try to create a library Based on the file name fileName (the library is represented by an object of the QLibraryPrivate class) // question: if the plug-in set path contains non-library common files (such as text files and images), do you also need to generate a library for them? So shouldn't non-library files be put in the plug-in set path? Library = QLibraryPrivate: findOrCreate (QFileInfo (fileName). canonicalFilePath (); if (! Library-> isPlugin () {// judge whether the library is a plug-in. if it is not a plug-in, skip the current library if (qt_debug_component () {qDebug () <library-> errorString; qDebug () <"not a plugin";} library-> release (); // release the database continue ;} // if the library is a plug-in, continue to run QStringList keys; bool metaDataOk = false; QString iid = library-> metaData. value (QLatin1String ("IID ")). toString (); if (iid = QLatin1String (d-> iid. constData (), d-> iid. size ())){// If the IID of the plug-in library is the same as the IID of the plug-in set corresponding to the current QFactoryLoader object, set the metaDataOk flag and // read the MetaData: Keys field in the meta information of the plug-in library, this field is a JSON array type and // stores several string-type keywords. These keywords are prefixed to the string list keys. QJsonObject object = library-> metaData. value (QLatin1String ("MetaData ")). toObject (); metaDataOk = true; // set the metaDataOk flag to 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) {// If the metaDataOk flag is not set, the IID does not match. Skip the library plug-in-> release (); continue;} int keyUsageCount = 0; // ing count, count the keywords mapped to the library // The third-layer small loop, traverse all keywords stored in the Keys field in the library metadata // This loop creates a ing table (part of) from the database to the keyword 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 c Onst QString & key = keys. at (k); // obtain the key of the k-th keyword. // If the ing table has already mapped the key, obtain the database previous mapped to it and the Qt version prev_qt_version, // compare with the Qt version qt_version of the current library. If prev_qt_version is higher than the current QT version and qt_version is not later than the current QT version, the key of the keyword is remapped to the library. // If the key has not been mapped to the ing table, map it directly to 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) {// if the key is not mapped to the ing table (! Previous), or the Qt version number of the database mapped to the key is not appropriate, // The key is mapped to the library. D-> keyMap [key] = library; ++ keyUsageCount; // ing count plus 1 }}// if the ing count is greater than 0 or MetaData in the meta information of the library: if the Keys field is blank (no keyword), add the library to the library List d-> libraryList if (keyUsageCount | keys. isEmpty () d-> libraryList + = library; // The else library-> release () ;}# else Q_D (QFactoryLoader) is created here ); if (qt_debug_component () {qDebug () <"QFactoryLoader: QFactoryLoader () ignoring" <d-> iid <"since plugins are disabled in static builds ";} # endif}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.