I'm using db4o to store different objects in an android application. I get json contents from a sever then them into Objects (contacts here) that I store in a db4o file.
ObjectContainer mainDB = Db4oEmbedded.openFile(dbConfig(), DB_MAIN);
mainDB.store(aContact);
Now if I directly make a query to get them back from the DB, it works.
try {
ObjectSet<Contact> result = db.queryByExample(Contact.class);
contacts = new ArrayList<Contact>(result);
} catch (Exception e) {
e.printStackTrace();
}
I can loop on the arraylist and the contacts are there, I can print their names and other fiels. But now if I close de db :
mainDB.close();
If I reopen it with the same code, there's nothing inside anymore, contacts.size() returns 0.
What's wrong? Any idea? Thanks in advance...
If you try a SODA query rather than querybyexample it works?
What's the result of Environment.getExternalStorageState()?
There might be a problem with the dir.
What happens if you try Context.getExternalFilesDir() as directory or getExternalStoragePublicDirectory()?
What happens if you use this dir when opening the container?
ctx.getDir("data", 0) + "/" + "db.file";
where ctx is the app context.
Related
I have a database saved in my apps assets folder and I copy the database using the below code when the app first opens.
inputStream = mContext.getAssets().open(Utils.getDatabaseName());
if(inputStream != null) {
int mFileLength = inputStream.available();
String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();
// Save the downloaded file
output = new FileOutputStream(filePath);
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = inputStream.read(data)) != -1) {
total += count;
if(mFileLength != -1) {
// Publish the progress
publishProgress((int) (total * 100 / mFileLength));
}
output.write(data, 0, count);
}
return true;
}
The above code runs without problem but when you try to query the database you get an SQLite: No such table exception.
This issue only occurs in Android P, all earlier versions of Android work correctly.
Is this a known issue with Android P or has something changed?
Was having a similar issue, and solved this adding this to my SQLiteOpenHelper
#Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
db.disableWriteAheadLogging();
}
Apparently Android P sets the PRAGMA Log thing different. Still no idea if will have side effects, but seems to be working!
My issues with Android P got solved by adding
'this.close()' after this.getReadableDatabase() in createDataBase() method as below.
private void createDataBase() throws IOException {
this.getReadableDatabase();
this.close();
try {
copyDataBase();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
This issue seems to lead to a crash much more often on Android P than on previous versions, but it's not a bug on Android P itself.
The problem is that your line where you assign the value to your String filePath opens a connection to the database that remains open when you copy the file from assets.
To fix the problem, replace the line
String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();
with code to get the file path value and then close the database:
MySQLiteOpenHelper helper = new MySQLiteOpenHelper();
SQLiteDatabase database = helper.getReadableDatabase();
String filePath = database.getPath();
database.close();
And also add an inner helper class:
class MySQLiteOpenHelper extends SQLiteOpenHelper {
MySQLiteOpenHelper(Context context, String databaseName) {
super(context, databaseName, null, 2);
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
I ran into a similar issue. I was copying a database but not from an asset. What I found is that the problem had nothing to do with my database file copying code at all. Nor did it have to do with files left open, not closed, flushing or syncing. My code typically overwrites an existing unopen database. What appears to be new/diffferent with Android Pie and different from previous releases of Android, is that when Android Pie creates a SQLite database, it sets journal_mode to WAL (write-ahead logging), by default. I've never used WAL mode and the SQLite docs say that journal_mode should be DELETE by default. The problem is if I overwrite an existing database file, let's call it my.db, the write-ahead log, my.db-wal, still exists and effectively "overrides" what's in the newly copied my.db file. When I opened my database, the sqlite_master table typically only contained a row for android_metadata. All the tables I was expecting were missing. My solution is to simply set journal_mode back to DELETE after opening the database, especially when creating a new database with Android Pie.
PRAGMA journal_mode=DELETE;
Perhaps WAL is better and there's probably some way to close the database so that the write-ahead log doesn't get in the way but I don't really need WAL and haven't needed it for all previous versions of Android.
Unfortunately, the accepted answer just "happens to work" in very concrete cases, but it doesn't give a consistently working advice to avoid such an error in Android 9.
Here it is:
Have single instance of SQLiteOpenHelper class in your application to access your database.
If you need to rewrite / copy the database, close the database (and close all connections to this database) using SQLiteOpenHelper.close() method of this instance AND don't use this SQLiteOpenHelper instance anymore.
After calling close(), not only all connections to the database are closed, but additional database log files are flushed to the main .sqlite file and deleted. So you have one database.sqlite file only, ready to be rewritten or copied.
After copying / rewriting etc. create a new singleton of the SQLiteOpenHelper, which getWritableDatabase() method will return new instance of the SQLite database! And use it till next time you will need your database to be copied / rewritten...
This answer helped me to figure that out: https://stackoverflow.com/a/35648781/297710
I had this problem in Android 9 in my AndStatus application https://github.com/andstatus/andstatus which has quite large suite of automated tests that consistently reproduced "SQLiteException: no such table" in Android 9 emulator before this commit:
https://github.com/andstatus/andstatus/commit/1e3ca0eee8c9fbb8f6326b72dc4c393143a70538 So if you're really curious, you can run All tests before and after this commit to see a difference.
Solution without disabling the WAL
Android 9 introduces a special mode of SQLiteDatabase called Compatibility WAL (write-ahead loggin) that allows a database to use "journal_mode=WAL" while preserving the behavior of keeping a maximum of one connection per database.
In Detail here:
https://source.android.com/devices/tech/perf/compatibility-wal
The SQLite WAL mode is explained in detail here:
https://www.sqlite.org/wal.html
As of the official docs the WAL mode adds a second database file called databasename and "-wal". So if your database is named "data.db" it is called "data-wal.db" in the same directory.
The solution is now to save and restore BOTH files (data.db and data-wal.db) on Android 9.
Afterwards it is working as in earlier versions.
I had the same thing I had an application in version 4 of android, and when updating my mobile that has android 9, then I was 2 days trying to find the error, thanks for the comments in my case I just had to add this.close ();
private void createDataBase () throws IOException {
this.getReadableDatabase ();
this.close ();
try {
copyDataBase ();
} catch (IOException e) {
throw new RuntimeException (e);
}
}
ready running for all versions !!
First, thank you for posting this question. I had the same thing happen. All was working well, but then when testing against Android P Preview I was getting crashes. Here's the bug that I found for this code:
private void copyDatabase(File dbFile, String db_name) throws IOException{
InputStream is = null;
OutputStream os = null;
SQLiteDatabase db = context.openOrCreateDatabase(db_name, Context.MODE_PRIVATE, null);
db.close();
try {
is = context.getAssets().open(db_name);
os = new FileOutputStream(dbFile);
byte[] buffer = new byte[1024];
while (is.read(buffer) > 0) {
os.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
throw(e);
} finally {
try {
if (os != null) os.close();
if (is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The issue I ran into was this code works just fine BUT in SDK 28+ openOrCreateDatabase no longer automatically creates the android_metadata table for you. So if you do a query of "select * from TABLE" it will not find that TABLE because the query starts to look after the "first" table which should be the metadata table. I fixed this by manually adding the android_metadata table and all was well. Hope someone else finds this useful. It took forever to figure out because specific queries still worked fine.
Similar issue, only Android P device affected. All previous versions no problems.
Turned off auto restore on Android 9 devices.
We did this to troubleshoot. Would not recommend for production cases.
Auto restore was placing a copy of the database file in the data directory before the copy database function is called in the database helper. Therefore the a file.exists() returned true.
The database that was backed up from the development device was missing the table. Therefore "no table found" was in fact correct.
Here's the perfect solution for this problem:
Just override this method in your SQLiteOpenHelper class:
#Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
db.disableWriteAheadLogging();
}
}
It seems that you don't close the output stream. While it probably does not explain why the db is not really created (unless Android P added a multi MB buffer) it is a good practice to use a try-with-resource, something like :
// garantees that the data are flushed and the resources freed
try (FileOutputStream output = new FileOutputStream(filePath)) {
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = inputStream.read(data)) != -1) {
total += count;
if (mFileLength != -1) {
// Publish the progress
publishProgress((int) (total * 100 / mFileLength));
}
output.write(data, 0, count);
}
// maybe a bit overkill
output.getFD().sync();
}
In version P, the major change is WAL (Write Ahead Log). The following two steps are required.
Disable the same by the following line in config.xml in the values folder under resources.
false
Make the following change in the DBAdapter class in createDatabase method. Otherwise phones with earlier Android versions crash.
private void createDataBase() throws IOException {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.P) {
this.getWritableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
The issue occurring in Android Pie,
Solution is:
SQLiteDatabase db = this.getReadableDatabase();
if (db != null && db.isOpen())
db.close();
copyDataBase();
Simplest answer to use following line for Database file path in Android PIE and above:
DB_NAME="xyz.db";
DB_Path = "/data/data/" + BuildConfig.APPLICATION_ID + "/databases/"+DB_NAME;
I have a database saved in my apps assets folder and I copy the database using the below code when the app first opens.
inputStream = mContext.getAssets().open(Utils.getDatabaseName());
if(inputStream != null) {
int mFileLength = inputStream.available();
String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();
// Save the downloaded file
output = new FileOutputStream(filePath);
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = inputStream.read(data)) != -1) {
total += count;
if(mFileLength != -1) {
// Publish the progress
publishProgress((int) (total * 100 / mFileLength));
}
output.write(data, 0, count);
}
return true;
}
The above code runs without problem but when you try to query the database you get an SQLite: No such table exception.
This issue only occurs in Android P, all earlier versions of Android work correctly.
Is this a known issue with Android P or has something changed?
Was having a similar issue, and solved this adding this to my SQLiteOpenHelper
#Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
db.disableWriteAheadLogging();
}
Apparently Android P sets the PRAGMA Log thing different. Still no idea if will have side effects, but seems to be working!
My issues with Android P got solved by adding
'this.close()' after this.getReadableDatabase() in createDataBase() method as below.
private void createDataBase() throws IOException {
this.getReadableDatabase();
this.close();
try {
copyDataBase();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
This issue seems to lead to a crash much more often on Android P than on previous versions, but it's not a bug on Android P itself.
The problem is that your line where you assign the value to your String filePath opens a connection to the database that remains open when you copy the file from assets.
To fix the problem, replace the line
String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();
with code to get the file path value and then close the database:
MySQLiteOpenHelper helper = new MySQLiteOpenHelper();
SQLiteDatabase database = helper.getReadableDatabase();
String filePath = database.getPath();
database.close();
And also add an inner helper class:
class MySQLiteOpenHelper extends SQLiteOpenHelper {
MySQLiteOpenHelper(Context context, String databaseName) {
super(context, databaseName, null, 2);
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
I ran into a similar issue. I was copying a database but not from an asset. What I found is that the problem had nothing to do with my database file copying code at all. Nor did it have to do with files left open, not closed, flushing or syncing. My code typically overwrites an existing unopen database. What appears to be new/diffferent with Android Pie and different from previous releases of Android, is that when Android Pie creates a SQLite database, it sets journal_mode to WAL (write-ahead logging), by default. I've never used WAL mode and the SQLite docs say that journal_mode should be DELETE by default. The problem is if I overwrite an existing database file, let's call it my.db, the write-ahead log, my.db-wal, still exists and effectively "overrides" what's in the newly copied my.db file. When I opened my database, the sqlite_master table typically only contained a row for android_metadata. All the tables I was expecting were missing. My solution is to simply set journal_mode back to DELETE after opening the database, especially when creating a new database with Android Pie.
PRAGMA journal_mode=DELETE;
Perhaps WAL is better and there's probably some way to close the database so that the write-ahead log doesn't get in the way but I don't really need WAL and haven't needed it for all previous versions of Android.
Unfortunately, the accepted answer just "happens to work" in very concrete cases, but it doesn't give a consistently working advice to avoid such an error in Android 9.
Here it is:
Have single instance of SQLiteOpenHelper class in your application to access your database.
If you need to rewrite / copy the database, close the database (and close all connections to this database) using SQLiteOpenHelper.close() method of this instance AND don't use this SQLiteOpenHelper instance anymore.
After calling close(), not only all connections to the database are closed, but additional database log files are flushed to the main .sqlite file and deleted. So you have one database.sqlite file only, ready to be rewritten or copied.
After copying / rewriting etc. create a new singleton of the SQLiteOpenHelper, which getWritableDatabase() method will return new instance of the SQLite database! And use it till next time you will need your database to be copied / rewritten...
This answer helped me to figure that out: https://stackoverflow.com/a/35648781/297710
I had this problem in Android 9 in my AndStatus application https://github.com/andstatus/andstatus which has quite large suite of automated tests that consistently reproduced "SQLiteException: no such table" in Android 9 emulator before this commit:
https://github.com/andstatus/andstatus/commit/1e3ca0eee8c9fbb8f6326b72dc4c393143a70538 So if you're really curious, you can run All tests before and after this commit to see a difference.
Solution without disabling the WAL
Android 9 introduces a special mode of SQLiteDatabase called Compatibility WAL (write-ahead loggin) that allows a database to use "journal_mode=WAL" while preserving the behavior of keeping a maximum of one connection per database.
In Detail here:
https://source.android.com/devices/tech/perf/compatibility-wal
The SQLite WAL mode is explained in detail here:
https://www.sqlite.org/wal.html
As of the official docs the WAL mode adds a second database file called databasename and "-wal". So if your database is named "data.db" it is called "data-wal.db" in the same directory.
The solution is now to save and restore BOTH files (data.db and data-wal.db) on Android 9.
Afterwards it is working as in earlier versions.
I had the same thing I had an application in version 4 of android, and when updating my mobile that has android 9, then I was 2 days trying to find the error, thanks for the comments in my case I just had to add this.close ();
private void createDataBase () throws IOException {
this.getReadableDatabase ();
this.close ();
try {
copyDataBase ();
} catch (IOException e) {
throw new RuntimeException (e);
}
}
ready running for all versions !!
First, thank you for posting this question. I had the same thing happen. All was working well, but then when testing against Android P Preview I was getting crashes. Here's the bug that I found for this code:
private void copyDatabase(File dbFile, String db_name) throws IOException{
InputStream is = null;
OutputStream os = null;
SQLiteDatabase db = context.openOrCreateDatabase(db_name, Context.MODE_PRIVATE, null);
db.close();
try {
is = context.getAssets().open(db_name);
os = new FileOutputStream(dbFile);
byte[] buffer = new byte[1024];
while (is.read(buffer) > 0) {
os.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
throw(e);
} finally {
try {
if (os != null) os.close();
if (is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
The issue I ran into was this code works just fine BUT in SDK 28+ openOrCreateDatabase no longer automatically creates the android_metadata table for you. So if you do a query of "select * from TABLE" it will not find that TABLE because the query starts to look after the "first" table which should be the metadata table. I fixed this by manually adding the android_metadata table and all was well. Hope someone else finds this useful. It took forever to figure out because specific queries still worked fine.
Similar issue, only Android P device affected. All previous versions no problems.
Turned off auto restore on Android 9 devices.
We did this to troubleshoot. Would not recommend for production cases.
Auto restore was placing a copy of the database file in the data directory before the copy database function is called in the database helper. Therefore the a file.exists() returned true.
The database that was backed up from the development device was missing the table. Therefore "no table found" was in fact correct.
Here's the perfect solution for this problem:
Just override this method in your SQLiteOpenHelper class:
#Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
db.disableWriteAheadLogging();
}
}
It seems that you don't close the output stream. While it probably does not explain why the db is not really created (unless Android P added a multi MB buffer) it is a good practice to use a try-with-resource, something like :
// garantees that the data are flushed and the resources freed
try (FileOutputStream output = new FileOutputStream(filePath)) {
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = inputStream.read(data)) != -1) {
total += count;
if (mFileLength != -1) {
// Publish the progress
publishProgress((int) (total * 100 / mFileLength));
}
output.write(data, 0, count);
}
// maybe a bit overkill
output.getFD().sync();
}
In version P, the major change is WAL (Write Ahead Log). The following two steps are required.
Disable the same by the following line in config.xml in the values folder under resources.
false
Make the following change in the DBAdapter class in createDatabase method. Otherwise phones with earlier Android versions crash.
private void createDataBase() throws IOException {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.P) {
this.getWritableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
The issue occurring in Android Pie,
Solution is:
SQLiteDatabase db = this.getReadableDatabase();
if (db != null && db.isOpen())
db.close();
copyDataBase();
Simplest answer to use following line for Database file path in Android PIE and above:
DB_NAME="xyz.db";
DB_Path = "/data/data/" + BuildConfig.APPLICATION_ID + "/databases/"+DB_NAME;
I was modifying the libcore on Andorid for debugging purpose.
It took a lot of time to build even for a variable change.
Is it possible to pass the data to libcore of android?
(so I can change the data in the running time).
I tried System.getProperty() but the data could not cross process.
I also tried SystemProperties.get() but it seems it can not be used in libcore (it could not find the package and symbol).
Does anyone know how to pass data to the libcore on Android?
Thanks Nativ.
JNI is doable but a little complicated for me.
Finally, I used a simple, easy but stupid way to do that.
I created a file and saved my parameter in this file, and get the data from libcore.
It is a stupid way but worked for me for debugging.
Now I don't need to rebuild libcore and It saved much for me.
You can use reflection on class android.os.SystemProperties to get System Properties at runtime.
Code example:
public static String getSystemProperty(String key) {
String value = "";
try {
Class clazz = Class.forName("android.os.SystemProperties");
if (clazz != null) {
Object object = clazz.newInstance();
value = (String) (clazz.getMethod("get", String.class).invoke(object, key));
} else {
System.err.println(TAG + ", getSystemProperty: Class is null.");
}
} catch (Exception e) {
e.printStackTrace();
}
return value;
}
I need to implement a service in android that must be able to monitor a folder to detect a certain file and read what it contains. I'm having a strange behavior with my code and I can't find the reason. This is my relevant code.
public void onCreate(){
lectorFichCSV = new LectorFichCSV(); //object to read CSV files
ftpFileObserver = new FileObserver(filePath.getAbsolutePath()){
public void onEvent(int event, String file) {
if((FileObserver.CREATE & event) != 0){
Log.i("INFO: ", filePath.getAbsolutePath() + "/" + file + " is created");
if(file.substring(0,3).equals("RVE")){ //If file is created and the one I expect
try{
Log.i("INFO: ", "We have a RVE answer");
is = new FileInputStream(filePath + "/" + file);
lineaVent = lectorFichCSV.parseCSVFileAsList(is); //Get information in a list
//Get dao from ORMLite
dao = getHelper().getLineaVentDao();
Iterator<String[]> iterator = lineaVent.iterator();
if(iterator.hasNext()){
String[] aux = iterator.next();
Log.i("INFO:", "CodLineaVent "+aux[0]);
if(aux[2].equals("S")){
//Update DB information accordin to my file
UpdateBuilder<LineaVent, Integer> updateBuilder = dao.updateBuilder();
updateBuilder.where().eq("_id", aux[0]);
updateBuilder.updateColumnValue("valido", true);
updateBuilder.updateColumnValue("saldo", true);
updateBuilder.update();
lineaVent.clear();
}else if(aux[2].equals("N")){
UpdateBuilder<LineaVent, Integer> updateBuilder = dao.updateBuilder();
updateBuilder.where().eq("_id", aux[0]);
updateBuilder.updateColumnValue("saldo", false);
updateBuilder.update();
lineaVent.clear();
}
File fileToDel = new File(filePath + "/" + file);
fileToDel.delete();
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}
}
I debugged the code and sometimes is working and sometimes I get lineaVent.size() == 0. I'm going crazy with this, I'm thinking, is it possible that events occurs faster than the creation of my file? that would be the reason when I tried to parse my CSV file into my List object is size = 0? In that case I'm not getting any FileNotFoundException.
Any help will be appreciate. Thank you.
I am not an expert with the inotify POSIX API that, IIRC, underlies FileObserver. However, given that there are separate events for CREATE, MODIFY, and CLOSE_WRITE, it stands to reason that the CREATE event is solely for file creation -- in other words, allocating a new entry in the filesystem for the file. That would either create an empty file, or perhaps a file with some initial load of bytes, but where other MODIFY calls might be needed to write out the full contents. CLOSE_WRITE would then be called to indicate that whoever was writing to the file has now closed their file handle.
Hence, if you are watching for some file to be created, to read it in, watch for CREATE, then watch for CLOSE_WRITE on that same file, and then try to read it, and see if that works better.
The process seemed quite simplistic at first, but there must be something that I am missing going forward with this task. There was a settings file that I wanted to create local to my application for storing a whole bunch of data (not preference worthy). I ended up saving the file with the following code snippet.
protected File createSettingsFileLocation(String fileNameF)
{
File directoryFile = context_.getDir("settings", Context.MODE_PRIVATE);
File settingsFile;
settingsFile = new File(directoryFile, fileNameF);
if (!settingsFile.exists())
{
try
{
settingsFile.createNewFile();
} catch(IOException e)
{
Log.e(MyConstants.LOG_TAG, "Could not create the file as intended within internal storage.");
return null;
}
}
return settingsFile;
}
and then proceeded to retrieve the file later by looking for it locally with the following code snippets.
public String getCurrentFileContainingSettings()
{
List<String >settingFilesInFolder = getLocalStorageFileNames();
if (settingFilesInFolder == null || settingFilesInFolder.isEmpty())
{
return null;
}
String pathToCurrentSettingsFile = settingFilesInFolder.get(0);
return pathToCurrentSettingsFile;
}
protected List<String> getLocalStorageFileNames()
{
return Arrays.asList(context_.fileList());
}
However, the settingFilesInFolder always returns no entries, so I get null back from the getCurrentFileContainingSettings(). As what I could see from the documentation it seems as thought I was doing it right. But, I must be missing something, so I was hoping that someone could point something out to me. I could potentially hard-code the file name once it has been created within the system in a preference file for access later the first time that the settings are created, but I shouldn't have to do something like that I would think.
fileList() only looks in getFilesDir(), not in its subdirectories, such as the one you created via getDir(). Use standard Java file I/O (e.g., list()) instead.