I get this error after going to this activity, starting a new one, coming back. It doesn't happen when I first load the activity. functionally everything works... but I still get this error.
ERROR/Cursor(1059): Finalizing a
Cursor that has not been deactivated
or closed. database =
/data/data/com.roger.testapp/databases/data,
table = null, query = SELECT MAX(_id)
FROM record
03-02 16:47:21.835:
ERROR/Cursor(1059):
android.database.sqlite.DatabaseObjectNotClosedException:
Application did not close the cursor
or database object that was opened
here
In onCreate:
mDbHelper = new CommonDbAdapter(this);
mDbHelper.open();
fillData();
fillData() calls fetchNote in my dbhelper, the cursor is used for a couple things, if rowId == 0, that means I didn't select an item to get into this activity and I want to get the last row in that table. if rowId = something else, then I grab that row. I think the problem is in here somewhere, I'm just not sure.
public Cursor fetchNote(long rowId, String table, String columns) throws SQLException {
if (rowId == 0) {
String query = "SELECT MAX(_id) FROM record";
Cursor cursor = mDb.rawQuery(query, null);
rowId = 0;
if (cursor.moveToFirst())
{
rowId = cursor.getInt(0);
}
}
Cursor mCursor =
mDb.query(true, table, new String[] {KEY_ROWID,
columns}, KEY_ROWID + "=" + rowId, null,
null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
In onDestroy:
super.onDestroy();
if (mDbHelper != null) {
mDbHelper.close();
}
Also, I am startManagingCursor
Don't close your database in onDestroy. Close it immediately after you're done using it(make sure every cursor is closed before the database is closed). onDestroy may not be called when you expect.
Also, close your Cursor object after you're done using it.
Edits:
Since your activity is managing your Cursor, you may consider stop managing it and closing everything in the onPause method, and in onResume open everything up and fillData once again. If you could change your code around so you dont rely on your activity managing the cursor, you wouldn't need to hold into open database objects and worry about them.
I would recommend not returning a cursor. It's a scarce resource.
In n-tiered Java EE, the best practice is to close all persistence resources (Connection, Statement and ResultSet) in the scope of the method in which they were created. Map a ResultSet into an object or collection and close the ResultSet.
I don't know if there's something special about Android that would invalidate this.
Take a look at the activity lifecycle - you can see that when you navigate away from your activity (onPause) if another activity needs memory your process is killed, but when your user then returns to the activity, it will hit onCreate again. This time you are opening a NEW CommonDbAdapter (but you never closed the original one) - thus when you eventually hit onDestroy you would only close the very latest mDbHelper.
As stated by others, your best method is not to pass the cursor back from the DBHelper, create an object with your data, close the cursor and pass your object back. Also close the DBHelper once you have used it, you can always create another if you need to use it again.
Related
This sourch code for my app:
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM Note where Username = '"+acc+"'", null);
//Cursor cursor = db.rawQuery("SELECT * FROM Note ", null);
if (cursor.moveToFirst()) {
do {
Note_DTO note = new Note_DTO();
note.setId(Integer.parseInt(cursor.getString(0)));
note.setDate(cursor.getString(1));
note.setUser(cursor.getString(2));
note.setContent(cursor.getString(3));
NoteList.add(note);
} while (cursor.moveToNext());
}
This function return nothing , but in debug mode , every single item was set .
Should it return a list of what i selected , i don't understand why and how it's not . Thanks for your help .
Use getReadableDatabase() for query purposes as follows.
SQLiteDatabase db = this.getReadableDatabase();
Please Read SQLiteOpenHelper documentation here for getReadableDatabase()
Create and/or open a database. This will be the same object returned
by getWritableDatabase() unless some problem, such as a full disk,
requires the database to be opened read-only. In that case, a
read-only database object will be returned. If the problem is fixed, a
future call to getWritableDatabase() may succeed, in which case the
read-only database object will be closed and the read/write object
will be returned in the future.
Read SQLiteOpenHelper documentation here for getWritableDatabase()
Create and/or open a database that will be used for reading and
writing. The first time this is called, the database will be opened
and onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int)
and/or onOpen(SQLiteDatabase) will be called.
Once opened successfully, the database is cached, so you can call this
method every time you need to write to the database. (Make sure to
call close() when you no longer need the database.) Errors such as bad
permissions or a full disk may cause this method to fail, but future
attempts may succeed if the problem is fixed.
According to the getWritableDatabase() documentation, it clearly mentions that it will cache the database. So it's possible to retrieve data from a cached copy of your database. As same as make sure you've closed the database connection after your transaction.
Finally your code would be like this
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM Note where Username = '"+acc+"'", null);
if (cursor.moveToFirst()) {
do {
Note_DTO note = new Note_DTO();
note.setId(Integer.parseInt(cursor.getString(0)));
note.setDate(cursor.getString(1));
note.setUser(cursor.getString(2));
note.setContent(cursor.getString(3));
NoteList.add(note);
} while (cursor.moveToNext());
}
Try this
SQLiteDatabase database = this.getReadableDatabase();
String selectQuery = "SELECT * FROM Note where Username " + " LIKE '%" + acc + "%'";
Cursor cursor = database.rawQuery(selectQuery, null);
int count = cursor.getCount();
database.close();
if(count==0)
{
return result;
}
else
{
if (cursor.moveToFirst()) {
do {
Note_DTO note = new Note_DTO();
note.setId(Integer.parseInt(cursor.getString(0)));
note.setDate(cursor.getString(1));
note.setUser(cursor.getString(2));
note.setContent(cursor.getString(3));
NoteList.add(note);
} while (cursor.moveToNext());
}
I've got an error trying to do different test on a DBTest from sunshine App on android studio. I can´t figure out whats going on.
Here is the error message
junit.framework.AssertionFailedError
at com.example.juandavid.sunshine.data.TestDb.insertLocation(TestDb.java:196)
at com.example.juandavid.sunshine.data.TestDb.testLocationTable(TestDb.java:117)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1729)
junit.framework.AssertionFailedError
at com.example.juandavid.sunshine.data.TestDb.insertLocation(TestDb.java:196)
at com.example.juandavid.sunshine.data.TestDb.testWeatherTable(TestDb.java:130)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1729)
I'm learning and this is a new kind of error for me because I´m also trying to learn tables, databases and testing at the same time.
And here is the code:
package com.example.juandavid.sunshine.data;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.test.AndroidTestCase;
public class TestDb extends AndroidTestCase {
public static final String LOG_TAG = TestDb.class.getSimpleName();
// Since we want each test to start with a clean slate
void deleteTheDatabase() {
mContext.deleteDatabase(WeatherDbHelper.DATABASE_NAME);
}
/*
This function gets called before each test is executed to delete the database. This makes
sure that we always have a clean test.
*/
public void setUp() {
deleteTheDatabase();
}
public void testLocationTable() {
// First step: Get reference to writable databas
//line 117 com.example.juandavid.sunshine.data.TestDb.testLocationTable
insertLocation();
}
public void testWeatherTable() {
// First insert the location, and then use the locationRowId to insert
// the weather. Make sure to cover as many failure cases as you can.
deleteTheDatabase();
//line 130 com.example.juandavid.sunshine.data.TestDb.testWeatherTable error when inserLocation called
long locationRowId = insertLocation();
assertFalse("Error: Location Not Inserted Correctly", locationRowId == -1L);
// Instead of rewriting all of the code we've already written in testLocationTable
// we can move this code to insertLocation and then call insertLocation from both
// tests. Why move it? We need the code to return the ID of the inserted location
// and our testLocationTable can only return void because it's a test.
// First step: Get reference to writable database
WeatherDbHelper dbHelper= new WeatherDbHelper(mContext);
SQLiteDatabase db =dbHelper.getWritableDatabase();
// Create ContentValues of what you want to insert
// (you can use the createWeatherValues TestUtilities function if you wish)
ContentValues weatherValues = TestUtilities.createWeatherValues(locationRowId);
// Insert ContentValues into database and get a row ID back
long weatherRowId = db.insert(WeatherContract.WeatherEntry.TABLE_NAME, null, weatherValues);
assertTrue(weatherRowId != -1);
// Query the database and receive a Cursor back
Cursor weatherCursor = db.query(
WeatherContract.WeatherEntry.TABLE_NAME, // Table to Query
null, // leaving "columns" null just returns all the columns.
null, // cols for "where" clause
null, // values for "where" clause
null, // columns to group by
null, // columns to filter by row groups
null // sort order
);
// Move the cursor to a valid database row
assertTrue("Error: No records returned from location query", weatherCursor.moveToFirst()); // No se econtro nada
// Validate data in resulting Cursor with the original ContentValues
// (you can use the validateCurrentRecord function in TestUtilities to validate the
// query if you like)
TestUtilities.validateCurrentRecord("testInsertReadDb weatherEntry failed to validate",
weatherCursor, weatherValues);
assertFalse( "Error: More than one record returned from weather query",
weatherCursor.moveToNext() );
// Finally, close the cursor and database
weatherCursor.close();
dbHelper.close();
}
public long insertLocation() {
WeatherDbHelper dbHelper = new WeatherDbHelper(mContext);
SQLiteDatabase db = dbHelper.getWritableDatabase();
// Second Step: Create ContentValues of what you want to insert
// (you can use the createNorthPoleLocationValues if you wish)
ContentValues testValues = TestUtilities.createNorthPoleLocationValues();
// Third Step: Insert ContentValues into database and get a row ID back
long locationRowId;
//Line 196 com.example.juandavid.sunshine.data.TestDb.insertLocation(TestDb.java:196)
locationRowId = db.insert(WeatherContract.LocationEntry.TABLE_NAME, null, testValues);
// Verify we got a row back.
assertTrue(locationRowId != -1);
// Data's inserted. IN THEORY. Now pull some out to stare at it and verify it made
// the round trip.
// Fourth Step: Query the database and receive a Cursor back
// A cursor is your primary interface to the query results.
Cursor cursor = db.query(
WeatherContract.LocationEntry.TABLE_NAME, // Table to Query
null, // all columns
null, // Columns for the "where" clause
null, // Values for the "where" clause
null, // columns to group by
null, // columns to filter by row groups
null // sort order
);
// Move the cursor to a valid database row and check to see if we got any records back
// from the query
assertTrue( "Error: No Records returned from location query", cursor.moveToFirst() );
// Fifth Step: Validate data in resulting Cursor with the original ContentValues
// (you can use the validateCurrentRecord function in TestUtilities to validate the
// query if you like)
TestUtilities.validateCurrentRecord("Error: Location Query Validation Failed",
cursor, testValues);
// Move the cursor to demonstrate that there is only one record in the database
assertFalse( "Error: More than one record returned from location query",
cursor.moveToNext() );
// Sixth Step: Close Cursor and Database
cursor.close();
db.close();
return locationRowId;
}
}
The test function insertLocation() doesn´t generate that error and is called by the testLocationTable and then again by the testWeatherTable.
Looking forward to your kind reply.
Regards,
Juan
In my DataBase Adapter I have a list of methods that puts data, upgrades data
and retrieves data. In each method I Instatiate like this
SQLiteDatabase db = this.getWritableDatabase();
I used to close the database on every method and my app used to crash. Then I left them open in every method so the crashing stopped.
Does the line of code open several database connections when I use several methods with the same line?
Is there a better approach to open the database?
Here's one of my methods
public int getAntalRows() throws Exception {
SQLiteDatabase db = this.getWritableDatabase();
Cursor mCount= db.rawQuery("SELECT COUNT (*) FROM " + TABLE_PRODUCTS,
null);
mCount.moveToFirst();
int x= mCount.getInt(0);
mCount.close();
return x;
}
Rather than have to rewrite a class that extends SQLiteOpenHelper every time I want an app with a database component, I'm trying to make a database utility library project. Is this possible?
I call the class DBUtil. It extends SQLiteOpenHelper, overrides onCreate & onUpgrade, and has functions for inserting and selecting rows. It compiles to a jar file.
I include this jar file in another app, DBUtilTests, wherein I create the database, specifying the database name, give it table names and column names for each table. When I get to the part where I insert rows and then do a select to verify the rows are there, nothing is returned.
In the select function in DBUtil, the cursor object is always null for some reason. I've tried creating it both of the following ways:
1:
String tableName="whatever";
//getColumnNamesForTable is a local function that returns the column names
// for a given table in an ArrayList of Strings
final ArrayList<String> columnNamesForTable = getColumnNamesForTable(tableName);
final String[] columns = columnNamesForTable.toArray(new String[columnNamesForTable.size()]);
SQLiteDatabase database = this.getReadableDatabase();
Cursor cursor = database.query(tableName, columns, null, null, "", "", "");
2:
String selectQuery = "SELECT * FROM " + tableName;
Cursor cursor = database.rawQuery(selectQuery, null);
Is there anything wrong in the above code, and if not, is what I'm trying to do possible?
I'm creating an application and I have problems with Cursor. I have an SQLiteDatabase that returns me a Cursor when I try to fetch the values with this function:
public Cursor fetchOption(long rowId) throws SQLException {
Cursor mCursor = mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
KEY_TITLE, KEY_BODY}, KEY_ROWID + "=" + rowId, null,
null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
I don't know how to obtain the value of the field in the Cursor. If I do that like so:
String a = mOptionDb.fetchOption(0).getColumnName(0).toString();
String b = mOptionDb.fetchOption(0).getColumnName(1).toString();
String c = mOptionDb.fetchOption(0).getColumnName(2).toString();
I only obtain the name of the columns (_id, title, body) but not the values. Any suggestions on how to achieve this?
I think you can forget about checking for null.
Instead check if there is data and then access the columns using the cursor:
Cursor cursor = fetchOption(0);
if (cursor.moveToFirst()) // data?
System.out.println(cursor.getString(cursor.getColumnIndex("title"));
cursor.close(); // that's important too, otherwise you're gonna leak cursors
It might also make sense to read an Android tutorial. The notepad tutorial seems to fit the bill: http://developer.android.com/guide/tutorials/notepad/index.html
You can use the Cursor's get* methods to retrieve values from the result:
long id = cursor.getLong(cursor.getColumnIndex("_id"));
long title = cursor.getString(cursor.getColumnIndex("title"));
...
Better practice is obviously to use constants (often provided by ContentProviders) instead of calls to getColumnIndex with hardcoded strings.
You can use this mechanism.
Cursor record=db.test(finalDate);
if(record.getCount()!=0){
if(record.moveToFirst()){
do{
Imgid1=record.getString(record.getColumnIndex(Database.PHOTO));
}while(record.moveToNext());
}
record.close();
}