I have a SQLite database on my sdcard (/storage/sdcard/MyDir/mydb.sqlite) and try to write some data in it. Due to the Storage Access Framework I can not access the file directly and I need to get write access like it is described here. This grants me write access via a DocumentFile.
However, the database needs to accessed via JDBC. JDBC requires a String to the database file to establish a database connection. This leads to the following problems:
If I use the Java-Path I can access the database, but I have no writing permission. Even if the access was granted like described above.
If I use the Uri of the DocumentFile (having writing permission) JDBC does not find the database file.
I can place my database in the private directory of the application on the sdcard (/storage/sdcard/Android/data/my.application.package/) where I have writing permission. However, I would like to store the database outside that directory, since the database should be accessed by multiple applications.
I tried this suggestion, which works on the device storage, however does not work on the sdcard. On the sdcard I get this stacktrace:
android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 1294 SQLITE_CANTOPEN_ENOENT[1294]): Could not open database
at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:284)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:215)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:705)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:272)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:239)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:1292)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:1247)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:930)
at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:947)
When I access the database on the device storage (internal storage and external storage) I can write the database. Only if I access the database on the sdcard I have no writing permission.
Is there any possibility to get write access to specific files on the sdcard based on java.io.File like it is possible for DocumentFile?
Does anybody have a different idea how I can access the data on the sdcard with writing permission?
I have a SQLite database on my sdcard (/storage/sdcard/MyDir/mydb.sqlite)
Using removable storage as a transfer location for a SQLite database is OK. For example, using removable storage for backup and restore operations is fine.
However, for a live database, removable storage is very risky. On many devices, removable storage is removable, and you will suffer catastrophic problems if the user removes the storage while you are trying to write to your database.
And, as you are discovering, using removable storage for a live data is impractical.
the database needs to accessed via JDBC
Android has a built-in SQLite database API. Attempting to use Android's half-baked JDBC classes for accessing a SQLite database is very strange.
When I access the database on the device storage (internal storage and external storage) I can write the database
You will encounter problems on Android 10 and higher, as you no longer have direct filesystem access to most of external storage.
Is there any possibility to get write access to specific files on the sdcard based on java.io.File like it is possible for DocumentFile?
No, sorry. You do not have filesystem access to arbitrary locations on removable storage as of Android 4.4. Android 10 extends that to arbitrary locations on external storage. Either:
Limit yourself to the directories that you can access, or
Use removable storage only for backup/restore operations, with the live database in a more conventional location (getDatabasePath() on Context)
Related
As we all know starting Android Q (API=29) introduced new Scoped Storage concept, which basically prevents access to external files/dirs, except media files. Namely:
Programmer can use internal/app specific storage (no limits) - getFilesDir()
The only external storage can be accessed is via getExternalFilesDir(), but during app uninstall data will be deleted
There's option to access external storage via MediaStore, but it works only for media files
App still can read any external file via: getContentResolver().openInputStream(uri)
In my case I have app, which stores user data in SQLite database in app specific directory, database size can be up-to several gigs.
In all previous versions of my app users used to backup data to external storage/SD/USB and transfer those data to their new device, so they could continue to use data (it's encrypted database containing any kind of private data, including audio/video/documents and so on).
Question: how can I backup this database? I couldn't manage to find appropriate way to save/backup data to any external storage...
P.S. Android autobackup service is unworkable due to the size of database.
I want to create SQL db file on USB (otg) device. The problem is that on newer devices I access the USB using SAF so no real file path or handle any more.
All android sqlite openOrCreateDatabase methods need file or path which is not longer possible with SAF/DocumentFile.
The other options I was thinking about using sqlite alternatives but seems Realm doesn't support this either?
Any other ideas?
So I assume sqlite API will soon be updated to support DocumentFile or Uri?
To make it clear app works with large data on an usb stick, app also needs db to store some info on the usb. Of course I can alwasy create on device and latter copy on usb, but if the process stops in the middle data will be lost?
You can not store SQLite databases using SAF because SQLite does not support opening databases on (SAF) Uris, and probably never will for reasons explained in the linked answer.
You need to create the database in an accessible location, perform any read/write operations and then copy the database onto the USB drive. To avoid data loss, don't overwrite the target file (if it exists) and instead use a temporary file and rename that only after the complete operation is successful.
If you look at the all available storage access methods, the only other option is Context#getExternalFilesDirs which should not work because...
The returned paths do not include transient devices, such as USB flash drives connected to handheld devices.
However, for the devices I tested on this does not seem to be true, e.g. you actually can access USB devices using getExternalFilesDirs(). E.g. if you have a volume uuid 00A1-CC00 you can access the SAF Uri content://com.android.externalstorage.documents/tree/00A1-CC00%3A/document/00A1-CC00%3AAndroid%2Fdata%2Fcom.example%2Ffiles as /storage/00A1-CC00/Android/data/com.example/files.
If you absolutely need to do database operations without a temporary internal file, you may be able to use an h2 in memory database. This would require a modified BACKUP TO statement to allow writing to a SAF OutputStream.
Can I Import and export the SQLite database? means that I just want to carry the database. .DB file will be put on my sd card. The app will fetch data from a database at sd card and write the data on that database. After using the app. close the app and take a .db file to other devices and able to see the previous data and can manipulate that database.
You can get database file in SD-Card with use of ORM. But not using default SQLiteOPenHelper.
Check Litepal Framework: For more info check this https://github.com/LitePalFramework/LitePal
Define where the .db file should be. "internal" means the .db file
will be stored in the database folder of internal storage which no
one can access. "external" means the .db file will be stored in the
path to the directory on the primary external storage device where
the application can place persistent files it owns which everyone
can access. "internal" will act as default.
For example:
<storage value="external" />
I have some paths on sd card on Android 7, which are unwritable using instant access requiring, so I have to use StorageVolume.createAccessIntent and therefore use DocumentFile later instead of File. But the problem is that I need to use File, because db lib(realm), that I'm using, simply uses File object to store data. So the point is that I have to createDirectory and this directory(it's path) has to have writing access. How to do this using DocumentFile.createDirectory() or something like that?
So the point is that I have to createDirectory and this directory(it's path) has to have writing access
That is not supported.
The only direct filesystem access that you have to removable storage is via getExternalFilesDirs(), getExternalCacheDirs(), and getExternalMediaDirs() (all methods on Context). If these return 2+ items, the second and subsequent ones are locations on removable storage that are unique to your app and which you can read/write using classic File objects.
Otherwise, keep your Realm database on external storage, then copy it to removable storage at some appropriate point (e.g., user-initiated "export" or "backup" operation).
I'm currently familar with the concepts of an application-own storage location and the external storage location that resides on an SD-card. But I'm not sure what to do in this scenario:
I'm writing some kind of library/open source classes that can be used by other applications too. These classes download some data and have to cache them on a public place. Now when an other application that uses the same classes is started it first checks if the required data are already available or not. If yes, the cached data have to be used, elsewhere it downloads the data for its own and stores them at the same public location where others can access them too.
My question: what would be the best place for this kind of public data?
External storage is suitable for this because files in internal storage will be private to your application.
You can get the path to external storage with Environment.getExternalStorageDirectory();