While reading the sqlite methods to read a row using a cursor I get to know about that after retrieving the data form the cursor we should close the cursor to avoid any memory leak, but here I have a doubt that in the code below cursor.getCount() is called after closing the cursor? Isn't it wrong to retrieve the data after closing the cursor?
Could anyone can clear this doubt!
Thanks in advance!!
public int getContactsCount() {
String countQuery = "SELECT * FROM " + TABLE_CONTACTS;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(countQuery, null);
cursor.close();
// return count
return cursor.getCount();
}
Here is the source code for SQLiteCursor#close():
#Override
public void close() {
super.close();
synchronized (this) {
mQuery.close();
mDriver.cursorClosed();
}
}
And here is the source code for SQLiteCursor#getCount():
#Override
public int getCount() {
if (mCount == NO_COUNT) {
fillWindow(0);
}
return mCount;
}
As you can see, the row count appears to be stored in a variable mCount, and this value is not reset when the cursor is closed. This might make sense from an efficiency point of view, since it saves the need to clear out state unnecessarily.
So it appears that getting the count after closing the cursor does work, but you probably should not rely upon it because the Javadoc makes no such guarantees, and this behavior could change later on.
Cursor.getCount()
here in your query i think the code is not wrong because the getcount() method just return the number of rows in the cursor and we are not retrieving any data from the cursor
Related
For example code below. Do we need to close cursor? Do we better use try/catch/finally instead of using if()?
public int getCount() {
final Cursor countCursor = contentResolver.query(
AnalyticContract.CONTENT_URI,
new String[] {"count(*) AS count"},
null,
null,
null);
if (countCursor == null) {
return 0;
}
countCursor.moveToFirst();
final int count = countCursor.getInt(0);
return count;
}
The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource.
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
The answer I believe is primarily opinion-based. It depends I guess on coder's preference and the circumstances.
I have always preferred the if (cursor != null) or vice versa approach. Unless something truly spectacular has happened; which will be handled by throws Exception, I'd use if-else checks wherever I want the reader/reviewer to see which parts are really and truly exceptions and which are occurrences of different possible/valid scenarios.
This brings us to the current problem of Curosr and applying null checks.
AFAIK (since mostly a Cursor is related with a SQLiteDatabase) a ContentResolver.query() should never return a null Cursor if the query itself is valid unless in case of an invalid query which is a real exception and you should instead get an Exception.
So in my opinion the best approach would be using your example either
public int getCount() throws Exception {
Cursor countCursor;
try {
countCursor = contentResolver.query(
AnalyticContract.CONTENT_URI,
new String[] {"count(*) AS count"},
null,
null,
null);
countCursor.moveToFirst();
return countCursor.getInt(0);
}
finally {
cursor.close();
}
}
Or a variation where Exception is caught and handled within the method itself.
Now to answer your second question whether or not you should close() a Cursor: you should always close a Cursor. Whenever you don't have need for it. If you delve deeper into any of the Cursor.close() method-implementations. Since Curosr is an interface which in case of SQLite is implemented by SQLiteCursor you will notice that this method releases any and all allocations held by it.
I prefer to make a database helper class and through that database access becomes much much easier. Sample of a database helper Class -
public class DatabaseHelperClass extends SQLiteOpenHelper {
public DatabaseHelperClass(Context context)
{
super(context,"hakeem.db",null,1);
}
//Tables
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("Your SQL Query to create a table");
}
//Delete Tables
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table table_name if exists");
onCreate(db);
}
//Insertion of data into tables
long insertData(Various Parameters you like to pass for insertion)
{
SQLiteDatabase db=getWritableDatabase();
ContentValues values=new ContentValues();
values.put("col_name1",value);
values.put("col_name2",value);
values.put("col_name3",value);
values.put("col_name4",value);
return db.insert("signup_details",null,values);
}
//Delete record
public int deleteData(int id)
{
SQLiteDatabase sb=getWritableDatabase();
return sb.delete("hospital_details","id="+id,null);
}
//Update data in table
int updateData(Parameters you want to pass for update. Make sure you include a primary key)
{
ContentValues values=new ContentValues();
values.put("col_name1",value);
values.put("col_name2",value);
values.put("col_name3",value);
values.put("col_name4",value);
return getWritableDatabase().update("signup_details",values,"id="+id,null);
}
//Read data from tables
Cursor getSigninDetails() { return getWritableDatabase().rawQuery("select * from table_name",null); }
}
and to access results from the database-
private void getDataFromDatabase() {
Cursor cursor = db.getUserData();
if (cursor.moveToFirst()) {
do {
var_name1= cursor.getString(0);
var_name2= cursor.getString(1);
var_name3= cursor.getString(2);
var_name4= cursor.getString(3);
} while (cursor.moveToNext());
}
cursor.close();
}
with simple below code i get this error for close database or cursor:
Database﹕ close() was never explicitly called on database '/data/data/ir.tsms/databases/tsms'
android.database.sqlite.DatabaseObjectNotClosedException:
My Function:
public Boolean searchLastID( Long lastID){
SQLiteDatabase db = this.getReadableDatabase();
String selectQuery = "SELECT * FROM " + this.RECEIVE_FIELDS_TABLE + " WHERE lastId = ?" ;
String[] args = {String.valueOf(lastID)};
Cursor cursor = db.rawQuery(selectQuery, args);
//db.close();
return cursor.moveToFirst();
}
after uncommenting db.close();
Cursor﹕ Finalizing a Cursor that has not been deactivated or closed. database = /data/data/ir.tsms/databases/tsms, table = null, query = SELECT * FROM ReceiveFields WHERE lastId = ?
whats problem and how to resolve that? i can't find any document about this problem. Thanks
make your cursor a global variable
and inside your onDestory Method close your cursor and your database
#Override
protected void onDestroy() {
super.onDestroy();
cursor.close();
db.close();
}
As soon as you're done retrieving the data of your query from your database you should close the Cursor or at least deactivate it. (Keyword: Freeing resources)
Following two quotes from the Docs on the close() and deactivate() method:
#close()
Closes the Cursor, releasing all of its resources and making it
completely invalid. Unlike deactivate() a call to requery() will not
make the Cursor valid again.
#deactivate()
Deactivates the Cursor, making all calls on it fail until requery() is
called. Inactive Cursors use fewer resources than active Cursors.
Calling requery() will make the cursor active again.
I have a content provider that accesses my database which is fine if you need to deal with record sets but I need a method to return an integer denoting the number of records in a table
The method looks like this
public long getRecordCount(String TableName) {
SQLiteDatabase mDatabase = mOpenHelper.getReadableDatabase();
String sql = "SELECT COUNT(*) FROM " + TableName;
SQLiteStatement statement = mDatabase.compileStatement(sql);
long count = statement.simpleQueryForLong();
return count;
}
But I am unable to find any way of using this (Or any other method that does not return a cursor for that matter) in a content provider so where is the best place to put this method and how to call it?
Obviously I could do the really bad option of selecting all the records with a managed query and using the cursor.count result but that is one hugely inefficient way of dealing with this specific requirement
Thanks
You can also simply use "count(*)" as a projection in a call to your content providers URIs, as in the following helper method
public static int count(Uri uri,String selection,String[] selectionArgs) {
Cursor cursor = getContentResolver().query(uri,new String[] {"count(*)"},
selection, selectionArgs, null);
if (cursor.getCount() == 0) {
cursor.close();
return 0;
} else {
cursor.moveToFirst();
int result = cursor.getInt(0);
cursor.close();
return result;
}
}
One way you can access it is by using the call() method in the ContentResolver class. I can't seem to find much about how to actually use this on google, but my guess is that you should just have your getRecordCount() return a bundle with your result in it. Of course the easier thing to do would be something like what's described in this SO Post.
So I'm working on a game for android and I'm currently stuck at the 'load savegame' button in the main menu.
This button calls methods that read data from the database and write it into a resource class from which on this data will be accessed.
The problem is: I want to disable the load button if there are no rows in the tables, which means that no savegame exists.
To do this I used the following method:
public boolean checkForTables(){
boolean hasTables;
String[] column = new String[1];
column[0] = "Position";
Cursor cursor;
cursor = db.query("itemtable", column, null, null, null, null, null);
if(cursor.isNull(0) == true){
hasTables=false;
}else{
hasTables=true;
}
return hasTables;
As you can see it starts a query on one of the database tables and checks if the 0-column, which is the only one that should be in this cursor, is null. ATM I can't check logcat results on this call because I seem to have some problems with it, but it seems that the query throws an exception because the table is empty.
Any idea to check the tables for rows?
____________________EDIT______________
NOTE: I checked the database and it sure is empty
Okay I used a rawQuery on the table but the approach with count-statement produced an error, so I'm using
public boolean checkForTables(){
boolean hasTables;
Cursor cursor = db.rawQuery("SELECT * FROM playertable", null);
if(cursor.getCount() == 0){
hasTables=false;
if(cursor.getCount() > 0){
hasTables=true;
}
cursor.close();
return hasTables;
}
I'm using this method to decide whether or not to disable the loadGame-button which looks like this:
loadGame = (ImageButton) findViewById(R.id.loadButton);
loadGame.setEnabled(databaseAccess.checkForTables());
loadGame.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
databaseAccess.loadPlayer();
databaseAccess.loadItems();
databaseAccess.dropTables();
}
});
So if checkForTables gets a rowcount of != 0 it will return true and therefore enable the Button, or disable it if rowcount = 0
Amusingly, although the tables are empty, checkForTables() returns true because getCount() seems to return a != 0 value - I just don't get it.
Perform a query such as select count(*) from itemtable. This query will yield you a single integer result, containing the number of rows in that table.
For example:
Cursor cursor = db.rawQuery("SELECT count(*) FROM itemtable");
if (cursor.getInt(0) > 0) ... // there are rows in the table
-------------------------------------------------------------------------------------
Please note that the following edit was attempted by #PareshDudhat but was rejected by reviewers. I have not kept up with Android since this answer was posted, but a very brief bit of research suggests the edit (at least the change to how rawQuery() is called, I didn't inspect the moveToFirst() but #k2col's comment suggests it is required now as well) has merit.
Cursor cursor = db.rawQuery("SELECT count(*) FROM itemtable",null);
cursor.moveToFirst();
if (cursor.getInt(0) > 0) ... // there are rows in the table
What mah says will work. Another approach you could use in your current function is:
hasTables = cursor.moveToFirst());
Note that this approach is probably only better to use if you plan on using the results of the query if hasTables is in fact true.
Also, don't forget to close your cursor when you are done with it!
EDIT
I don't know if this is your problem but in your edit you are querying for all items from the playerTable instead of the itemTable as you did in the pre-edit. Is that your problem?
cursor.getCount()
return the number of rows in database table.
and then try
Toast.makeText(this,""+cursor.getCount(),Toast.LENGTHLONG).show();
and it will give you no of rows in database table
The accepted answer put me on the right track, but didn't compile because rawQuery's method signature has changed and the cursor wasn't advanced to the first row before being read.
Here's my solution which includes error handling and closes the cursor:
public static boolean isEmpty() {
boolean isEmpty;
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT count(*) FROM itemtable", null);
if (cursor.moveToFirst()) {
isEmpty = cursor.getInt(0) == 0;
} else {
// Error handling here
}
} catch (SQLException e) {
// Error handling here
} finally {
cursor.close();
}
return isEmpty;
}
So I'm trying to get the values from a SQLite database into a cursor, then pick a random value. I can read the cursor with getString() as I normally would in the method, but after it returns the cursor it doesn't work correctly. I don't know why..
Here's my method for getting the cursor from the database. It seems to work correctly.
public Cursor getRandomText(String Rating)
{
Cursor cursor = myDatabase.query("Elec0RandTexts", new String[] {"Message"}, "Rating=?",
new String[]{Rating}, null, null, null);
cursor.moveToFirst();
cursor.close();
return cursor;
}
Here's my code for reading the cursor after it's returned.
Cursor result = dbh.getRandomText(Rating);
result.moveToFirst();
int RandText = rand.nextInt(result.getCount());
result.moveToPosition(RandText);
Toast.makeText(getApplicationContext(), "" + result.getString(RandText), Toast.LENGTH_LONG).show();
result.close();
I'm probably making a stupid mistake and not realizing it, but I can't figure this out.
Thanks,
~Elec0
cursor.close(); // in getRandomText()
after that you cannot obtain any data from the cursor - it is closed. Remove this line.
You close() your Cursor before you return it. From where it is returned to, you are then attempting to call moveToFirst(). This cannot be done if the Cursor is closed.
In your getRandomText(String) method, you should return the meaningful data from your Cursor, rather than the Cursor object itself. That way, the method that created the Cursor can continue to close the Cursor as it should. (It should just happen at the end of the method)