Help with the Android Cursor - android

So I have a Cursor that is getting a result from a query to the application database. I know that there is at least one correct entry in the database since I was able to retrieve the string from the row previously.
I then changed some of the logic to accommodate the result and am suddenly getting null returned when calling c.getString(0). The relevant code is posted below.
I'm new to Android and Java, so I might be missing some subtlety that's causing the problem.
Cursor c = context.getContentResolver().query(tempJobName.build(),
null, null, null, null);
for (c.moveToFirst(); c.isAfterLast() == false; c.moveToNext())
{
Log.w(TAG, c.getString(0));
if (c.getString(0).equalsIgnoreCase(jobName))
{
existed = true;
break;
}
}

You might want to add more details. But since you did find some records, the solution for your problem is easy. Could you please replace this line with this? ( First column of table might change right :))
c.getString(0);
with
int columnIndex = c.getColumnIndex(COLUMN_NAME);// You could also use getcolumnIndexorThrow variant.
c.getString(columnIndex);
if this does not work, then your table does not have the column. You are simply barking up the wrong tree.

Related

Initialize SQLite Cursor before accessing data from it

I am trying to insert data into a SQLite DB once a notification is received via FCM. For debugging purpose I am also inserting a dummy data into my DB when SHow Token is clicked on the HomeScreen activity.
However am getting
"I am getting "Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it."
Link to my code: - GitHub
Can someone please go through my code and let me know where I am going wrong.
Note - I added below in HomeScreen.java,MyFirebaseMessagingService.java and NotificationDetails.java
private SQLiteDB dbHelper = new SQLiteDB(this);
since the suggested
private SQLiteDB dbHelper;
did not work for me
When I used above I kept on getting Nullpointer exception, so I figured since the SQLiteDB class constructor is accepting a context, so let me pass one, post which I did not get NullPointer Exception.
Now I did this without being fully aware of the concept on context which I have been trying to wrap my head around, but since am an extreme noob to android I am not able to grasp it just yet. I suspect it might have something to do with the context I am passing.
Can someone please help me here with detailed instructions on how to fix this issue, I have been through many other threads on this but was not able to fix hence after 5 hrs of going through multiple SO questions, I am posting this one.
Thanks in advance to everyone in the community for the help. :)
Edit
Upon suggestion by admins, I am including below snippet of my code.
Where I am calling the cursor
dbHelper.insertNotification("This is a notification");
//Check if the message contains data
Cursor rs = dbHelper.getAllNotifications();
rs.moveToFirst();
token_text.setText("Token: " +rs.getString((rs.getColumnIndex("NOTIFICATION_DETAILS"))));
Insert Notification Function in SQLiteDB.java
public boolean insertNotification(String notification){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(NOTIFICATION_DETAILS,notification);
db.insert(NOTIFICATION_TABLE_NAME,null,contentValues);
return true;
}
getAllNotifications function
public Cursor getAllNotifications() {
SQLiteDatabase db = this.getWritableDatabase();
Cursor res = db.rawQuery( "SELECT * FROM " + NOTIFICATION_TABLE_NAME, null );
return res;
}
Couldn't read row 0, col -1 from CursorWindow.
Is saying that you are attempting to get the column at offset -1 from row 0 (the first row). So you have provided an invalid offset (it cannot be an offset of -1, the offset must be 0 or greater and the maximum value will be 1 less than the number of columns in the Cursor).
The most likely cause, is that Cursor method getColumnIndex(the_column_name_as_a_string) will return -1 when the column passed to the method cannot be found in the Cursor. Noting that due to a bug column name is case sensitive.
As such your issue is that the Cursor does not contain a column name NOTIFICATION_DETAILS and as you have used * (all columns) then that column does not exist in the table.
By the looks of it you should be using the String variable NOTIFICATION_DETAILS so you probably need to use :-
token_text.setText("Token: " +rs.getString((rs.getColumnIndex(NOTIFICATION_DETAILS)))); //<<<<<<<<<< double quotation marks removed.
Additional
You should NEVER assume that moveToFirst (or any Cursor move???? method) actually does the move. You should ALWAYS check the returned value. It will be true if the move was successful otherwise it would be false.
Again note that the column name passed to the getColumnIndex method is case dependant.
As such you should use something like
:-
dbHelper.insertNotification("This is a notification");
//Check if the message contains data
Cursor rs = dbHelper.getAllNotifications();
if (rs.moveToFirst()) {
token_text.setText("Token: " +rs.getString((rs.getColumnIndex(NOTIFICATION_DETAILS))));
} else {
........ code here if anything need to be done if there are no rows extracted
}
Addition re comment :-
Cursor rs = dbHelper.getAllNotifications(); rs.moveToFirst(); do{ for
(int i = 0; i < rs.getColumnCount(); i++) {
notification_array.add(rs.getString((rs.getColumnIndex(NOTIFICATION_DETAILS))));
} }while (rs.moveToNext());
using the following is much simpler :-
Cursor rs = dbHelper.getAllNotifications();
while (rs.moveToNext()) {
notification_array.add(rs.getString((rs.getColumnIndex(NOTIFICATION_DETAILS))));
}

why c.moveToFirst() won't skip the first row

I always use the code below to get all the informations
But I have question that c.movetoNext lead to skip the first row of result set
Please tell me what really happens behind the surface so that I can fully understand it
SQLiteDatabase db;
Cursor c = db.query("pic", null, null, null, null,null, null);
c.moveToFirst();
while(c.movetoNext)
{
//do something
//example: String a=c.getString(0);
}
You're calling moveToFirst(), which positions the cursor on the first row, then your while loop calls moveToNext() before entering the loop, which results in the first row being skipped.
You can solve it with either a do...while loop, or a for loop such as
for( c.moveToFirst(); !c.isAfterLast(); c.moveToNext() ) {
// Stuff
}
A late answer, but hopefully useful to others finding this page. I've been using an: "if () do { } while();" loop. The "moveToFirst()" function returns false if no rows were returned in the cursor. The "moveToNext()" function returns false when past end of cursor:
Cursor c = db.query(......);
if (c.moveToFirst()) do {
data = c.getString(0) // eg. get value from first column in cursor
// do something more
} while (c.moveToNext());
c.close();
(which is similar to TactMayers' answer).
According to the Cursor documentation, "query... Returns: A Cursor object, which is positioned before the first entry". It further seems that queries get executed only when methods are called on the cursor. Hope that helps.
c.moveToFirst();
This line move the cursor to the first row.
while(c.movetoNext)
After running this line of code, the cursor will move to next row (row 2 for the first run of the while loop.) So by the time the code enter the loop, the cursor is pointing to row 2 already and can never access the first row.
Try replacing the while loop like this
do {
// do something
} while(c.movetoNext())
Actually, you do not have to call c.moveToFirst(); at all

Error when accessing cursor elements

I'm trying to get all the IDs in the table by cursor. The table has 4 rows, and when I try to access the cursor by any index aside from 0 , the App crashes.
Guys, the problem still exists and even c.getInt(0) doesn't work...I really dont know where my mistake is???
the logcat also suggests that the error might be comes from
Toast.makeText(getApplicationContext(), "id="+dbd.getIDs()[0], Toast.LENGTH_SHORT).show();
I mean c.getint(0) returns the id, c.getint(2) returns error. Here is the code:
public int []getIDs() {
SQLiteDatabase db = this.getReadableDatabase();
SQLiteCursor c = (SQLiteCursor) db.rawQuery("SELECT " + BaseColumns._ID + " FROM Demo ", null);
int []r = new int[c.getCount()];
c.moveToFirst();
do{
r[c.getPosition()] = c.getInt(0);
}while(c.moveToNext());
c.close();
db.close();
return r;
}
Your select is a projection onto the ID column (select columns from ..., columns are the column ids you are interested in, and you specified just one). Thus the answer just has one column, namely the ID. Any access to columns with index > 0 will not work.
To access the other columns name them in the projection in your query.
c.getInt(0) return only value of first colunn from current row.
try this code:
do{
int r = c.getInt(0);
Log.d("Your class name","id value = "+r);
}while(c.moveToNext());
You can imagine Cursor as a table. There are rows and columns. And a cursor is pointing on a particular row in this table. Thus, to get all id's you should move across all rows.
c.getInt(ind) In this statement index is pointing on the column with index ind. Thus, in your code you try to get the second and third column and according to your code there is no these column.
To get a proper code you should create a loop and traverse all rows of your cursor. Also you should use c.getInt(0) to get the columns values.
Assuming you are selecting the appropriate Data, your problem is that you're not preparing the Cursor to be iterated.
Before iterating, call:
c.moveToFirst();
Like this:
int []r = new int [c.getCount()];
c.moveToFirst();
do{
r[c.getPosition()] = c.getInt(0);
}while(c.moveToNext());
c.close();
db.close();
return r;
This is well indicated in LogCat. I can't remember how it's put, but the message is very suggestive. Please post as much of the Log as possible, especially the good bits.
Also, I modified 'r' to be an array. As it was you were only returning the value of the last row.

Android 3.0 Couldn't read row#, column# from cursor window

I have an application that runs fine on android 2.1, but when trying to transition it to 3.0 I get a cursor error that I'm not familar with.
Java.lang.IllegalStateException: Couldn't read row0, column -1 from
cursor window. Make sure cursor is initialized correctly before
accessing data from it.
All the data is storred in a SQLite database and this code works fine in android 2.1. Does a cursor have to be initialized differently in android 3.0?
Listed below is my code.
private void OpenGroupData(){
SQLiteDatabase db = openOrCreateDatabase(DATABASE_NAME,Context.MODE_PRIVATE,null);
Cursor cur = db.rawQuery("SELECT groupid FROM properties GROUP BY GroupID" + ";" , null);
LinearLayout glayout = (LinearLayout) findViewById(R.id.Grouplayout);
LinearLayout gwindow = (LinearLayout) findViewById(R.id.groupwindow);
TextView data = new TextView(this);
glayout.addView(data);
data.setText("");
int ID = cur.getColumnIndex("groupid");
int idvalue;
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
try{
// Check if our result was valid.
cur.moveToFirst();
if (cur != null) {
// Loop through all Results
do {data = new TextView(this);
data.setTextSize(20);
data.setClickable(true);
data.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
GroupClick(v);
}
});
glayout.addView(data);
idvalue = cur.getInt(ID);
data.setId(idvalue);
data.setText("Group: " + idvalue);
}while(cur.moveToNext());
}
cur.close();
db.close();
} catch(Exception e) {
Toast.makeText(getApplicationContext(), "Open Group Exception: " + e.toString(), Toast.LENGTH_SHORT).show();
}
}
I ran into the same error message earlier this day. All it turned out to was that I made a typo in the column name. So, if it still applies, you could go and check the column name for typo's. Note that its case sensitive aswell. Error for me was along the lines of:
//Trew error
c.getColumnIndex("ArticleNumber");
//Was correct
c.getColumnIndex("Articlenumber");
Alright I figured it out. For some reason when trying to transistion my application to 3.0 when my cursor goes and gets the column index for a field, in this case ("groupid") it is returning a value of -1. When the Cursor tries to start at -1 it crashes because it can't find a record at row(0), column(-1). So my fix was to just add one to the column index when getting the id. see below.
int ID = cur.getColumnIndex("groupid") + 1;
int idvalue;
By adding 1 to the Column index it seems to have solved the problem.
If getColumnIndex returns -1, then the column doesn't exist.
Otherwize it returnes zero-based column index.
-1 is the _id column which every sqlite table should have as is required by the android framework. If you have to add +1 to the index you are going wrong somewhere.
The -1 value returns form getColumnIndex(columnName) if 'columnName' can't be found. Check the column names

Android error: java.lang.IllegalStateException: trying to requery an already closed cursor

environment (Linux/Eclipse Dev for Xoom Tablet running HoneyComb 3.0.1)
In my app I'm using the camera (startIntentForResult()) to take a picture. After the picture is taken I get the onActivityResult() callback and am able to load a Bitmap using a Uri passed via the "take picture" intent. At that point my activity is resumed and I get an error trying to reload the images into a gallery:
FATAL EXCEPTION: main
ERROR/AndroidRuntime(4148): java.lang.RuntimeException: Unable to resume activity {...}:
java.lang.IllegalStateException: trying to requery an already closed cursor
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2243)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1019)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:126)
at android.app.ActivityThread.main(ActivityThread.java:3997)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: trying to requery an already closed cursor
at android.app.Activity.performRestart(Activity.java:4337)
at android.app.Activity.performResume(Activity.java:4360)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2205)
... 10 more
The only cursor logic I'm using is that after the image is taken I convert the Uri to a file using the following logic
String [] projection = {
MediaStore.Images.Media._ID,
MediaStore.Images.ImageColumns.ORIENTATION,
MediaStore.Images.Media.DATA
};
Cursor cursor = activity.managedQuery(
uri,
projection, // Which columns to return
null, // WHERE clause; which rows to return (all rows)
null, // WHERE clause selection arguments (none)
null); // Order-by clause (ascending by name)
int fileColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (cursor.moveToFirst()) {
return new File(cursor.getString(fileColumnIndex));
}
return null;
Any ideas what I'm doing wrong?
Looks like the managedQuery() call is deprecated in the Honeycomb API.
Doc for managedQuery() reads:
This method is deprecated.
Use CursorLoader instead.
Wrapper around query(android.net.Uri, String[], String, String[], String)
that the resulting Cursor to call startManagingCursor(Cursor) so that the
activity will manage its lifecycle for you. **If you are targeting HONEYCOMB
or later, consider instead using LoaderManager instead, available via
getLoaderManager()**.
Also I noticed that I was calling cursor.close() after the query which I guess is a no-no. Found this really helpful link as well. After some reading I came up with this change that seems to work.
// causes problem with the cursor in Honeycomb
Cursor cursor = activity.managedQuery(
uri,
projection, // Which columns to return
null, // WHERE clause; which rows to return (all rows)
null, // WHERE clause selection arguments (none)
null); // Order-by clause (ascending by name)
// -------------------------------------------------------------------
// works in Honeycomb
String selection = null;
String[] selectionArgs = null;
String sortOrder = null;
CursorLoader cursorLoader = new CursorLoader(
activity,
uri,
projection,
selection,
selectionArgs,
sortOrder);
Cursor cursor = cursorLoader.loadInBackground();
For the record, here's how I fixed this in my code (which runs on Android 1.6 and up): The problem in my case was that I was inadvertently closing managed cursors by calling CursorAdapter.changeCursor(). Calling Activity.stopManagingCursor() on the adapter's cursor before changing the cursor solved the problem:
// changeCursor() will close current one for us: we must stop managing it first.
Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); // *** adding these lines
stopManagingCursor(currentCursor); // *** solved the problem
Cursor c = db.fetchItems(selectedDate);
startManagingCursor(c);
((SimpleCursorAdapter)getListAdapter()).changeCursor(c);
FIX: use context.getContentResolver().query instead of activity.managedQuery.
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, PROJECTION, null, null, null);
} catch(Exception e) {
e.printStackTrace();
}
return cursor;
I've created this question here as I could not comment on the last answer (comments disabled for some reason). I thought opening a new thread on this will only complicate things.
I get application crashes when I go from Activity A to Activity B and then going back to Activity A. This doesn't happen all the time - only sometimes and I am having hard time finding EXACTLY where this happens. All happens on the same device (Nexus S) but I don't believe this is a device issue.
I have few questions regarding #Martin Stine's answer.
In the documentation it says about changeCursor(c);: "Change the underlying cursor to a new cursor. If there is an existing cursor it will be closed". So why do I have to stopManagingCursor(currentCursor); - isn't that redundant?
When I use the code offered by #Martin Stine I get a null pointer exception. The reason for that is that in the first "run" of the application ((SimpleCursorAdapter)getListAdapter()) will evaluate to NULL because no cursor was yet to be created. Sure I could check if I DON'T get a null and only then try stopping to manage the cursor but finally I decided to place my `stopManagingCursor(currentCursor); in the onPause() method of this activity. I thought this way I will for sure have a cursor to stop managing and I should do it just before I leave the Activity to a different one. The issue - I am using several cursors (one to fill up a text of an EditText field and the other for a list view) in my activity I guess not all of them are related to a ListAdapter cursor -
How do I know which one to stop managing? If I have 3 different list views?
Should I close all of them during onPause()?
How do I get a list of all my opened cursors?
So many questions... Hope anyone could help.
When I get to onPause() I do have a cursor to stop managing but I am yet to determine if this solves the problem as this error shows up sporadically.
Many thanks!
AFTER SOME INVESTIGATION:
I found something interesting that might give the answer to the "mysterious" side of this issue:
Activity A uses two cursors: one to fill up an EditText field. The other is to populate a ListView.
When moving from Activity A to Activity B and coming back, the field + ListView in Activity A must be filled up again. It seems like the EditText field will never have an issue with that. I couldn't find a way to get the EditText field's current cursor (like in Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();) and reason tells me that the EditText field will not keep it. On the other hand the ListView will "remember" its cursor from last time (from before Activity A -> Activity B). In addition, and this is the strange thing, the Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); will have a different ID after Activity B -> Activity A and all this WITHOUT me ever calling
Cursor currentCursor = ((SimpleCursorAdapter)getListAdapter()).getCursor();
stopManagingCursor(currentCursor);
I guess in some cases, when the system needs to free resources, the cursor will be killed and when Activity B -> Activity A, the system will still try to use this old dead cursor, which will result in an Exception. And in other cases the system will come up with a new cursor that is still alive and thus no Exception will occur. This might explain why this shows up only sometimes. I guess this is hard to debug due to the difference of application speed when either running OR debugging the application. When debugging, it takes more time and thus might give the system time to come up with a new cursor or vice versa.
In my understanding this makes the usage of
Cursor currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor();
stopManagingCursor(currentCursor);
As recommended by #Martin Stine a must in SOME cases and redundant in OTHERS: if I return to the method and the system is trying to use a dead cursor, one must create a new cursor and replace it in the ListAdapter, otherwise I get angry app users with a crashed app. In another case when the system will find itself a new cursor - the lines above are redundant as they invalidate a good cursor and create a new one.
I guess in order to prevent this redundancy I would need something like this:
ListAdapter currentListAdapter = getListAdapter();
Cursor currentCursor = null;
Cursor c = null;
//prevent Exception in case the ListAdapter doesn't exist yet
if(currentListAdapter != null)
{
currentCursor = ((SimpleCursorAdapter)currentListAdapter).getCursor();
//make sure cursor is really dead to prevent redundancy
if(currentCursor != null)
{
stopManagingCursor(currentCursor);
c = db.fetchItems(selectedDate);
((SimpleCursorAdapter)getListAdapter()).changeCursor(c);
}
else
{
c = db.fetchItems(selectedDate);
}
}
else
{
c = db.fetchItems(selectedDate);
}
startManagingCursor(c);
I would love to hear what you think about this!
Just add the following code at the end of the cursor block..
try {
Cursor c = db.displayName(number);
startManagingCursor(c);
if (!c.moveToFirst()) {
if (logname == null)
logname = "Unknown";
System.out.println("Null " + logname);
} else {
logname = c.getString(c
.getColumnIndex(DataBaseHandler.KEY_NAME));
logdp = c.getBlob(c
.getColumnIndex(DataBaseHandler.KEY_IMAGE));
// tvphoneno_oncall.setText(logname);
System.out.println("Move name " + logname);
System.out.println("Move number " + number);
System.out.println("Move dp " + logdp);
}
stopManagingCursor(c);
}
This problem plagued me for a long time and I finally came up with a simple solution that works like a charm on all versions of Android. First, don't use startManagingCursor() since it is evidently buggy and deprecated in any case. Secondly, close a Cursor as soon as possible after you are done with it. I use try and finally to insure that the Cursor gets closed under all circumstances. If your method must return a Cursor then the calling routine is responsible for closing it as soon as possible.
I used to leave cursors open for the lifetime of an Activity but I have since abandoned that for this transactional approach. Now my app is very stable and does not suffer from "Android error: java.lang.IllegalStateException: trying to requery an already closed cursor" when switching Activities even though they access the same database.
static public Boolean musicReferencedByOtherFlash(NotesDB db, long rowIdImage)
{
Cursor dt = null;
try
{
dt = db.getNotesWithMusic(rowIdImage);
if ( (dt != null)
&& (dt.getCount() > 1))
return true;
}
finally
{
if (dt != null)
dt.close();
}
return false;
}

Categories

Resources