How to read an existing SQLite database in Android using Qt?

别说谁变了你拦得住时间么 提交于 2020-07-03 10:04:10

问题


I have been developing an app in Qt on Ubuntu which works with an SQLite database and several GB of files. Everything is working well in Ubuntu and Windows, but... when I try to port to Android I'm having some problems. I've placed the database.db file and other files on an SD card, installed it in my target device, and finally was able to "find" it with this code:

QString dbName;
#ifdef Q_OS_ANDROID
  QAndroidJniObject androidContext = QtAndroid::androidContext();
  QAndroidJniObject            dir = QAndroidJniObject::fromString(QString(""));
  QAndroidJniObject path = androidContext.callObjectMethod("getExternalFilesDir",
                                                           "(Ljava/lang/String;)Ljava/io/File;",
                                                           dir.object());
  // qInfo() << "Path: " + path.toString();
  dbName = path.toString()+"/database.db";
#else
  QSettings *sp = new QSettings();
  dbName = sp->value( "dbName", "/database.db" ).toString();
  sp->deleteLater();
#endif

  qDebug( "Database::Database(%s)", qPrintable( dbName ) );
  const QString DRIVER("QSQLITE");
  if ( !QSqlDatabase::isDriverAvailable(DRIVER) )
    { qDebug( "QSqlDatabase::isDriverAvailable(%s) is false", qPrintable(DRIVER) ); }
   else
    { db = QSqlDatabase::addDatabase(DRIVER);
      db.setDatabaseName( dbName );
      QSqlDriver *driver = QSqlDatabase::database().driver();
      if ( !db.open() )
        { qDebug( "Error opening database %s: %s", qPrintable( dbName ), qPrintable( db.lastError().text() ) );
        }
       else
        { qDebug( "Database opened." );
        }
    }

So, finally by using JNI to track down the SD card folder I'm getting the database to report that it is successfully opened. However, now when I try to read my first table I'm getting failure of the query.exec() in Android (that doesn't happen in Linux/Windows):

  QSqlQuery query(db);
  query.prepare( "SELECT time FROM history ORDER BY time DESC;" );
  if ( !query.exec()  ) { qDebug( "ERROR: %s, %s", qPrintable( query.lastError().text() ), qPrintable( query.executedQuery() ) ); return; }

this returns:

D MyApp: ERROR: No query Unable to fetch row, SELECT time FROM history ORDER BY time DESC;

in Android, but works in other OSs. All subsequent attempts to read from the database also fail in Android. MyApp is a console app, I eventually want to run it as a service, but before taking that leap I'd like to see it work in "simple mode." The Application Output does throw a couple of warnings (marked W) before the program starts, but they don't seem connected?

I zygote  : Late-enabling -Xcheck:jni
W System  : ClassLoader referenced unknown path:
I Qt      : qt started
I zygote  : Do partial code cache collection, code=26KB, data=23KB
I zygote  : After code cache collection, code=26KB, data=23KB
I zygote  : Increasing code cache capacity to 128KB
I zygote  : Do partial code cache collection, code=62KB, data=55KB
I zygote  : After code cache collection, code=62KB, data=55KB
I zygote  : Increasing code cache capacity to 256KB
D OpenGLRenderer: HWUI GL Pipeline
I Adreno  : QUALCOMM build                   : 94aeef9, Ie8d0be8cd4
I Adreno  : Build Date                       : 11/09/17
I Adreno  : OpenGL ES Shader Compiler Version: EV031.22.00.01
I Adreno  : Local Branch                     :
I Adreno  : Remote Branch                    :
I Adreno  : Remote Branch                    :
I Adreno  : Reconstruct Branch               :
W RenderThread: type=1400 audit(0.0:2413): avc: denied { search } for name="proc" dev="debugfs" ino=12448 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:qti_debugfs:s0 tclass=dir permissive=0
I Adreno  : PFP: 0x005ff087, ME: 0x005ff063
I OpenGLRenderer: Initialized EGL, version 1.4
D OpenGLRenderer: Swap behavior 2
I zygote  : Do full code cache collection, code=112KB, data=112KB
I zygote  : After code cache collection, code=110KB, data=100KB
D MyApp: Database::Database(/storage/emulated/0/Android/data/com.mangocats.myapp/files/database.db)
D MyApp: Database opened.
D MyApp: ERROR: No query Unable to fetch row, SELECT time FROM history ORDER BY time DESC;

This is with latest stable Android Studio downloaded and configured yesterday, JDK 1.8, latest Qt Creator downloaded and installed yesterday running on Ubuntu 18.04, Qt 5.14.2, targeting an Android 8.0 phone. Sample Qt apps and simple GUI apps I have run on the phone successfully, and the http server side of the console app is working as expected. I've given the app READ and WRITE EXTERNAL STORAGE permissions (even though many notes suggest they are not needed for these versions...)

What else do I have to do for Android to get the SQLite database working?


回答1:


Make sure the file exists

As already mentioned in the comments, before calling

db.setDatabaseName(dbName);

make sure that dbName refers to an existing file in the filesystem. Because otherwise QSqlDatabase::open() will silently create an empty SQLite3 database by that name. You would then see an error like yours, or also "Parameter count mismatch" when attempting the first query.

Or prevent open() from creating the file

If you rather want QSqlDatabase::open() to fail if the specified database does not exist and only require read-only access, you can utilize a side effect of connection option QSQLITE_OPEN_READONLY (source):

db.setConnectOptions("QSQLITE_OPEN_READONLY");
db.setDatabaseName(dbName);
if (!db.open()) {
    // ...
}

SQLite itself also has an option SQLITE_OPEN_READWRITE that would not restrict this solution to read-only access, but this option is not yet supported in the Qt interface.

And only use ordinary filesystem filenames

For reference, when calling QSqlDatabase::setDatabaseName() don't try to specify a file using the qrc:/ Qt resource scheme or assets:/ Qt-style Android assets access scheme. The method only accepts plain relative or absolute filesystem filenames (or their equivalent as SQLite-specific file: URIs). ((This is not mentioned in the documentation, but visible in the Qt source code here: Qt forwards the given filename to sqlite3_open_v2() and that only accepts filesystem filenames.))

The only way to access a database bundled with the application into the Android APK package is to first copy it to the application's data directory. Because that copy then has has an ordinary filesystem path. of the form /data/data/com.example.appname/files/database.sqlite.



来源:https://stackoverflow.com/questions/61314573/how-to-read-an-existing-sqlite-database-in-android-using-qt

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!