I'm migrating some code from Qt 6.2.2 to 6.9.0 under Android.
I have some code looking in the application folder for .so files (for potential plugins to be loaded). This code used to iterate the app folder using standard std::filesystem
code. It does not work anymore. I can iterate the HOME folder, but not the app folder. Surprisingly QDir
is able to do so, but my code doing this is pure C++/STL code, I cannot use Qt at this level.
Did something change in apk structure? Why STL cannot see the app folder? Is there any workaround?
Here is a very simple sample program:
#include <QApplication>
#include <QMainWindow>
#include <QLabel>
#include <QDebug>
#include <QDir>
#include <filesystem>
void listDirContent(QString path)
{
{
QDir dir(path);
qDebug() << "Using Qt, " << path << ":";
for (auto file : dir.entryList())
qDebug() << file;
}
{
std::filesystem::path std_path = path.toStdString();
qDebug() << "using std, " << std_path.c_str() << ":";
std::filesystem::directory_iterator itDir;
std::filesystem::directory_iterator itEnd;
std::filesystem::path Path;
try
{
itDir = std::filesystem::directory_iterator(std_path);
qDebug() << "Found files:";
for (; itDir != itEnd; itDir++)
{
qDebug() << QString(itDir->path().c_str());
}
qDebug() << "end files";
}
catch (const std::exception& e)
{
qDebug() << "FAILED" << e.what();
}
}
}
int main( int argc, char* argv[] )
{
QApplication app(argc, argv);
QMainWindow wnd;
wnd.setCentralWidget(new QLabel("Hello",&wnd));
wnd.show();
listDirContent(qApp->applicationDirPath());
listDirContent(QDir::homePath());
return app.exec();
}
This program outputs:
D/default (18701): Using Qt, "/data/app/org.qtproject.example.TestListDir-UzLzgFYV8gnNNAzUQkD1Lw==/base.apk!/lib/armeabi-v7a" :
D/default (18701): "armeabi-v7a"
D/default (18701): "libc++_shared.so"
D/default (18701): "libplugins_iconengines_qsvgicon_armeabi-v7a.so"
D/default (18701): "libplugins_imageformats_qgif_armeabi-v7a.so"
D/default (18701): "libplugins_imageformats_qico_armeabi-v7a.so"
D/default (18701): "libplugins_imageformats_qjpeg_armeabi-v7a.so"
D/default (18701): "libplugins_imageformats_qsvg_armeabi-v7a.so"
D/default (18701): "libplugins_platforms_qtforandroid_armeabi-v7a.so"
D/default (18701): "libplugins_styles_qandroidstyle_armeabi-v7a.so"
D/default (18701): "libQt6Core_armeabi-v7a.so"
D/default (18701): "libQt6Gui_armeabi-v7a.so"
D/default (18701): "libQt6Svg_armeabi-v7a.so"
D/default (18701): "libQt6Widgets_armeabi-v7a.so"
D/default (18701): "libTestListDir_armeabi-v7a.so"
D/default (18701): using std, /data/app/org.qtproject.example.TestListDir-UzLzgFYV8gnNNAzUQkD1Lw==/base.apk!/lib/armeabi-v7a :
D/default (18701): FAILED filesystem error: in directory_iterator::directory_iterator(...): No such file or directory ["/data/app/org.qtproject.example.TestListDir-FvVxa-eHXbBc9Zpa2Q0uGQ==/base.apk!/lib/armeabi-v7a"]
D/default (18701): Using Qt, "/data/user/0/org.qtproject.example.TestListDir/files" :
D/default (18701): "."
D/default (18701): ".."
D/default (18701): "profileInstalled"
D/default (18701): "profileinstaller_profileWrittenFor_lastUpdateTime.dat"
D/default (18701): using std, /data/user/0/org.qtproject.example.TestListDir/files :
D/default (18701): Found files:
D/default (18701): /data/user/0/org.qtproject.example.TestListDir/files/profileinstaller_profileWrittenFor_lastUpdateTime.dat
D/default (18701): /data/user/0/org.qtproject.example.TestListDir/files/profileInstalled
D/default (18701): end files
See that std::filesystem is unable to iterate /data/app/org.qtproject.example.TestListDir-UzLzgFYV8gnNNAzUQkD1Lw==/base.apk!/lib/armeabi-v7a
folder, it reports the folder does not exist, while Qt is able to iterate through it.
With Qt 6.2.2, it used to list the files from the app folder (which was slightly different, there was no "base.apk!" in path.
I reported a Qt bug: https://bugreports.qt.io/browse/QTBUG-136214
Does anyone have an idea how to workaround this issue? How to make std::filestem be able to look into the app folder?
Edit, std::filesystem
is able to iterate /data/app/org.qtproject.example.TestListDir-UzLzgFYV8gnNNAzUQkD1Lw==
, it sees base.apk
as being a file. Looks like QDir
supports a special case where apk file name is followed by !
and is able to go look into it as if it was actually a folder...
As you discovered, Android used to extract the native libraries from the APK to a plain directory. This default was changed to not extract them, but instead read libraries directly from the APK. If you change extractNativeLibraries
to true
you get back the old behavior, at the cost of more disk usage.
The application file path you received (/data/app/.../base.apk!/lib/armeabi-v7a
) uses the Android convention of a !
to indicate "a file path within this APK". The Android core libraries know how to work with these paths.
The Android C++ standard library, however, uses the normal opendir
and readdir
system calls and rightfully complains that /.../base.apk!/lib/armeabi-v7a
does not exist (as only /.../base.apk
exists on disk).
Finally, the reason why Qt's QDir supports this is because Qt registers an APK-aware "File engine" which understands this path notation and allows you to access APKs like regular directories. There are similar file engines for assets and Android's content URIs.