getReadableDatabase often, but not always returns null - android

I see in the Crashlytics-Logs of my android application a NullPointerException in this code:
try {
mSQLDBreader = this.getReadableDatabase();
} catch (SQLException e) {
if (mSQLDBreader != null) {
mSQLDBreader.close();
mSQLDBreader = this.getReadableDatabase();
}
}
mSQLDBreader... // NPE
As the previous developer isn't available any more, I don't know why it is tried two times, but the code seems to work sometimes, but often not. What can be reasons that this call returns null?
It seems like if this only happens on 2.3.x-devices, in my crashlogs all the affected devices are 2.3.5 and 2.3.6.

Do you have any threading going on of your own creation? Some code that might be resetting mSQLDBreader via another method. Perhaps that thread occasionally runs and trashes the value of mSQLDBreader before you use it?
Have you ever witnessed this on an emulator running 2.3.x?
I looked at google's code for getReadableDatabase, and I don't see a way that it could return null. Gory details are below if you are interested. Based on what I see, I would suspect either a multithreading bug in your code, or a bug introduced by customizations to the android code by the manufacturer of the devices you tested (if that is even plausible.)
Gory details All paths through getReadableDatabase invoke methods on the return object after creating it. So the value can't be null at that point. Otherwise the NPE would be raised from inside.
Here is a snippet of the 2.3.6 code for getReadableDatabase. Actual source is available on grepcode.
public synchronized SQLiteDatabase getReadableDatabase() {
if (mDatabase != null && mDatabase.isOpen()) {
return mDatabase; // The database is already open for business
}
if (mIsInitializing) { /* snip throw ISE */ }
try {
return getWritableDatabase();
} catch (SQLiteException e) {
// snip : throws or falls through below
}
SQLiteDatabase db = null;
try {
mIsInitializing = true;
String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);
// *** next line calls method on db. NPE would be here if db was null at this point. ***
if (db.getVersion() != mNewVersion) {
// snip throw.
}
onOpen(db);
Log.w(TAG, "Opened " + mName + " in read-only mode");
mDatabase = db;
return mDatabase;
} finally {
// snip : not relevant
}
}
Notice that getReadableDatabase usually just returns the result of getWritableDatabase. He looks like this:
public synchronized SQLiteDatabase getWritableDatabase() {
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
return mDatabase; // The database is already open for business
}
if (mIsInitializing) {
throw new IllegalStateException("getWritableDatabase called recursively");
}
// snip comment about locking
boolean success = false;
SQLiteDatabase db = null;
if (mDatabase != null) mDatabase.lock();
try {
mIsInitializing = true;
if (mName == null) {
db = SQLiteDatabase.create(null);
} else {
db = mContext.openOrCreateDatabase(mName, 0, mFactory);
}
int version = db.getVersion(); // ** method called on result!
// snip block that has more method calls and never nulls out db
onOpen(db);
success = true;
return db;
} finally {
// snip
mDatabase = db;
// snip rest of finally block that isn't relevant.
}
}
Lastly, it is important to note that both of these methods, as well as the close method of SqliteOpenHelper, are tagged with synchronize, so there is no way for one method to trash the state of the other if you have multiple threads calling these methods at the same time..

I have ran into similar problem in the past. It seems like keep closing and opening the database created this problem for me. If you are using the SQLLiteOpenHelper you should not be closing your database unless you have a really good reason to. See CommonsWare answer here which says
SQLiteOpenHelper holds onto the database you retrieve with
getReadableDatabase()/getWritableDatabase(), and the point is for you
to reuse that opened SQLiteDatabase object, particularly as you do
work across multiple threads.

open dataBase before use
if (mDatabase != null && mDatabase.isOpen()) {
return mDatabase; // The database is already open for business
} else {
return mDatabase.open();
}

Related

unable to open database file (code 14) beginTransactionUnchecked

I have search through SO and google but doesn't seem fit into my case.
My application randomly gives me this exception from multiple user logs, which never happen while I test it in the emulator nor my device.(I am running on Galaxy tab2 10.1 ,android OS 4.1.1.)
I tested it can be run successfully from development.The required android manifest permissions are in place.
Below are my codes and stack trace
stacktrace :
android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file (code 14)
android.database.sqlite.SQLiteConnection.nativeExecute(Native Method)
android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:678)
android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323)
android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298)
android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505)
android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416)
com.framework.dataaccess.GenericDAO.start(GenericDAO.java:78)
Below is my singleton that instantiate SQLitedatabase
protected static GenericDAO getInstance(Context ctx, String sql) {
if (instance == null) {
try {
instance = new GenericDAO(ctx, sql);
}
catch (Exception e) { e.printStackTrace(); }
}
if(db == null || !db.isOpen()){
try {
db = instance.getWritableDatabase();
}
catch (Exception e) { e.printStackTrace(); }
}
return instance;
}
and for Insertion, delete or update operation I would put in these start() and commit().
public void start() throws Exception{
try{
db.beginTransaction();
}catch(Exception e){
e.printStackTrace();
throw e;
}
}
public void commit() throws Exception{
try{
db.setTransactionSuccessful();
}
catch(Exception e){
e.printStackTrace();
throw e;
}
finally{
db.endTransaction();
}
}
public Cursor select(String table, String[] columns, String criteriaColValue){
return db.query(true, table, columns, criteriaColValue, null, null, null, null, null);
}
My code throws the exception from the dao select below. Which does not call the 'start()' method.
public ArrayList<Obj> getObjList(){
ChildDao dao = null;
Cursor cursor = null;
ArrayList<Obj> list = new ArrayList<Obj>();
try{
dao = new childDao(context);
cursor = dao.select(TABLE_NAME, columns, Criterea );
if(cursor!=null && cursor.moveToFirst()){
do{
list.add(myObjs);
} while(cursor.moveToNext());
}
}catch(Exception ex){
ex.printStackTrace();
}
finally{
if(cursor!=null)cursor.close();
}
return list;
}
I have no idea how the error hit and not able to reproduce it from development environment. Can anyone give me a guide where to look into ?
Thanks in advance.
This may be a little late, but hope this can give a clue for whoever gets this problem. I also had a similar hard to reproduce exception:
unable to open database file (code 14)
android.database.sqlite.SQLiteCantOpenDatabaseException: unable to open database file (code 14)
at android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(Native Method)
at android.database.sqlite.SQLiteConnection.executeForCursorWindow(SQLiteConnection.java:845)
It could be not because of database but has to do with extra data/files allocation somewhere in an application. In my case it was bad SoundPool code. Here are the details:
java.lang.RuntimeException: Could not read input channel file descriptors from parcel

How to insert bulk data in android sqlite database using ormlite efficiently

I'm trying to insert 100000 records in android sqlite database at a time. I'm using following two different methods.
private void bulkInsertDataBySavePoint(final List<User> users) {
log.debug("bulkInsertDataBySavePoint()");
DatabaseConnection conn = null;
Savepoint savepoint = null;
try {
conn = userDao.startThreadConnection();
savepoint = conn.setSavePoint("bulk_insert");
for (User user : users) {
userDao.create(user);
}
} catch (SQLException e) {
log.error("Something went wrong in bulk Insert", e);
} finally {
if (conn != null) {
try {
conn.commit(savepoint);
userDao.endThreadConnection(conn);
} catch (SQLException e) {
log.error("Something went wrong in bulk Insert", e);
}
}
}
}
And
private void bulkInsertDataByCallBatchTasks(final List<User> users) {
log.debug("bulkInsertDataByCallBatchTasks()");
try {
userDao.callBatchTasks(new Callable<Void>() {
#Override
public Void call() throws Exception {
for (User user : users) {
userDao.create(user);
}
return null;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
Both methods work fine. On average they take 140 seconds and take 60-65% CPU which is not ok, I think.
The idea is, I have to consume an api which will provide json data. I have to parse that json data and then insert into sqlite database for offline usage.
I'm looking for an efficient way to solve this issue.
Any thought?
I'm trying to insert 100000 records in android sqlite database at a time... On average they take 140 seconds and take 60-65% CPU which is not ok in my opinion.
Unfortunately I don't have an easy answer for you. You may have to do this sort of insert directly using raw SQL to achieve faster performance on the limited Android CPU. Once you have the data inserted then you can turn to ORMLite to query or manipulate the data faster.
I've had the same problem, and found a reasonable workaround. This took insert time from 2 seconds to 150ms:
final OrmLiteSqliteOpenHelper myDbHelper = ...;
final SQLiteDatabase db = myDbHelper.getWritableDatabase();
db.beginTransaction();
try{
// do ormlite stuff as usual, no callBatchTasks() needed
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
Hrm. Good idea #FarrukhNajmi. I've just added it to trunk. It will be in version 4.49.
#Gray Is it still unstable? when can we see it in maven?
And if com.j256.ormlite.dao.ForeignCollection#addAll make only one request it would be nice too.

Ormlite ObjectCache returning old data

I'm using Ormlite on Android and with the ObjectCache enabled, I get old data back after updating the table with an UpdateBuilder and a ColumnExpression. I have read through the doc and it does not warn against using the UpdateBuilder with the cache enabled.
The settings table should have just 1-5ish rows max. The updateColumnExpression seems like an easy way to allow only one of the rows to be true.
Is this the expected behavior?
public void setActiveSetting(String id)
{
try {
UpdateBuilder<Settings, Integer> updateBuilder2 = getHelper().getSettingsDao().updateBuilder();
updateBuilder2.updateColumnExpression("active", "id = " + id );
updateBuilder2.update();
} catch (SQLException e){
e.printStackTrace();
}
}
And this is the call that returns the outdated data:
public List<Settings> getSettings()
{
List<Settings> settings = null;
try {
settings = getHelper().getSettingsDao().queryForAll();
} catch (SQLException e) {
e.printStackTrace();
}
return settings;
}
And the settings DAO:
public Dao<Settings, Integer> getSettingsDao()
{
if (null == settingsDao) {
try {
settingsDao = getDao(Settings.class);
settingsDao.setObjectCache(true);
} catch (java.sql.SQLException e) {
e.printStackTrace();
}
}
return settingsDao;
}
Disabling the ObjectCache does return the correct data, but this data is fetched quite frequently, so I'd like to keep it.
Thanks
Is this the expected behavior?
Unfortunately, yes. If you had updated the object using dao.update(...); then the cache would know that the object needed to be refreshed. By using the UpdateBuilder to make mass changes to the table, there is no way for the cache to know which objects were affected.
You will need to clear the cache after your call to the UpdateBuilder finishes.

Reading SQL from file and SQLiteOpenHelper

I need to include an existing SQLiteDatabase in my Android app and I also want the ability to download and install a new db. I did some research and my first working solution came from here. I didn't like that solution, for one thing, it assumes that the database will always be in a fixed path and other oddities.
So, rather than putting the existing database file in the assets, I exported the database to a SQL file, read it in, and then in the onCreate() method of my SQLiteOpenHelper, I call a new method updateDatabase with an open DataInputStream with the file data.
I ran into a few issues, which I think I solved, but I'm sure I didn't think of all the issues, which are:
When SQLiteOpenHelper's onCreate method is called, the database has been created, it is open, and inTransaction() is true. As a result, if the imported sql file includes BEGIN TRANSACTION, an exception is thrown and if the sql String contains statements creating 'android_metadata' yet another exception. So, I added a simple search using String.contains() looking for these keywords, and set a boolean doExecute to false to avoid executing them. So a question is, is there a better SQL class or method to filter this, or even a better regexp method?
Similar issue with having unexpected line breaks in the SQL file. I read the file with readLine() and to look for line breaks, I simply use String.trim() on the line, then check for endsWith(";"). This puts some constraints on my input file, like not having multiple statements on a single line. So, is there a better way to pre-process SQL from a file?
Here's the code I use to create my db after I've gotten a DataInputStream from the assets resource or from a download:
public boolean updateDatabase(DataInputStream inStream, SQLiteDatabase db, boolean doClear) throws Error {
String sqlStatement = null;
boolean result = true;
boolean inOnCreate = true;
boolean wasInTransaction;
if(doClear) dropDatabase();
// if called from onCreate() db is open and inTransaction, else getWritableDatabase()
if(db == null) {
inOnCreate = false;
db = this.getWritableDatabase();
}
wasInTransaction = db.inTransaction(); // see NB below
boolean doExecute;
try {
while ((sqlStatement = inStream.readLine()) != null) {
// trim, so we can look for ';'
sqlStatement.trim();
if(!sqlStatement.endsWith(";")) {
continue; // line breaks in file, get whole statement
}
// NB - my file (exported from SQLite Database Browser starts with "BEGIN TRANSACTION;".
// executing this throws SQLiteException: cannot start a transaction within a transaction
// According to SQLiteDatabase doc for beginTransaction(), "Transactions can be nested"
// so this is a problem
// but... possibly it is an "exclusive transaction" ?
doExecute = true;
if(wasInTransaction) {
// don't execute BEGIN TRANSACTION; or COMMIT;
if((sqlStatement.contains("BEGIN" ) || sqlStatement.contains("begin" )) &&
(sqlStatement. contains("TRANSACTION") || sqlStatement.contains("transaction" ))) {
doExecute = false;
}
if(sqlStatement.contains("COMMIT") || sqlStatement.contains("commit")) {
doExecute = false;
}
} // inTransaction
// this statement could be in older databases, but this scheme doesn't need, can't have it
if(sqlStatement.contains("android_metadata")) {
doExecute = false;
}
if(doExecute) {
try {
db.execSQL(sqlStatement);
} catch (SQLException e) {
throw(new Error("Error executing SQL " + sqlStatement));
} // try/catch
} // doExecute
} // while()
} catch (IOException e) {
result = false; // which won't matter if we throw
throw(new Error("Error reading " + DB_SQL));
}
if(!inOnCreate) {
db.close();
}
return result;
}
Wouldn't want to have you cop out early on such ambitious and elegant implementations, but if you have your database already made and checked with a database browser and all, have you considered SQLite Asset Helper? If your main issue was being forced to use the asset folder, this method lib let's you use a file from any specified directory. Moreover, it allows for handling the raw .db file. Worth checking out.

Performance SQLite Issue - Can a codechange speed up things?

I use the following code to add rows to my database :
public void insert(String kern, String woord) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KERN, kern);
values.put(WOORD, woord);
db.insertOrThrow(TABLE_NAME, null, values);
return;
Currently, I'm invoking this insert() 3.455 times, to add all words to the database, using : insert("Fruits", "Banana"); It takes forever.
How can I change this code to work faster? I'm thinking in the line of foreach, but don't know how to implement.. Thanks!
/Edit; The solution provided by #hovanessyan works and will do the job. AND.. note that if you have a lot of lines that have to be put in, you might be confronted with the method exceeds max byte limit error. In that case, review the other solution, that suggests packing the database in the actual .APK file.
You can wrap-up those inserts into transaction.
db.beginTransaction();
try {
// do all the inserts here
//method call here, that does 1 insert; For example
addOneEntry(kern,woord);
...
db.setTransactionSuccessful();
} catch (SQLException e) {
//catch exceptions
} finally {
db.endTransaction();
}
private void addOneEntry(String kern, String woord) {
//prepare ContentValues
//do Insert
}
You can use bulkInsert:
ContentValues[] cvArr = new ContentValues[rows.size()];
int i = 0;
for (MyObject row : rows) {
ContentValues values = new ContentValues();
values.put(KERN, myObject.getKern());
values.put(WOORD, myObject.getWoord);
cvArr[i++] = values;
}// end for
resolver.bulkInsert(Tasks.CONTENT_URI, cvArr);
Using the tips of both hovanessyan and Damian (remind me to rep+1 you as soon as I reach 15 ;), I came up with the following solution:
For relatively small databases (<1,5Mb)
I created the database using SQLite Database Browser, and put it in my Assets folder.
Then, the following code copies the database to the device, if it's not already there:
boolean initialiseDatabase = (new File(DB_DESTINATION)).exists();
public void copyDB() throws IOException{
final String DB_DESTINATION = "/data/data/happyworx.nl.Flitswoorden/databases/WoordData.db";
// Check if the database exists before copying
Log.d("Database exist", "" + initialiseDatabase);
Log.d("Base Context", "" + getBaseContext());
if (initialiseDatabase == false) {
// Open the .db file in your assets directory
InputStream is = getBaseContext().getAssets().open("WoordData.db");
// Copy the database into the destination
OutputStream os = new FileOutputStream(DB_DESTINATION);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0){
os.write(buffer, 0, length);
}
os.flush();
os.close();
is.close();
}}
In my app, a portion of the database is User-customizable.
I call the code above in onStart() with :
try {
copyDB();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
So, when the user presses "reset database to standard" (in preferences screen), I just set the Boolean initialiseDatabase to "false" and wait for the user to go back to the main activity. (thus calling onstart and copying the original database).
I tried to call the Activity.copyDB() from the preferences.java. It's neater, because it doesn't require the user to go back to the main activity to rebuild the database. However, I get an error about not being able to call static references to non-static methods. I don't understand that, but will look into it.

Categories

Resources