Failed to read row 0, column 0 from a CursorWindow which has 0 rows, 64 columns

Janpan picture Janpan · Nov 5, 2014 · Viewed 8.2k times · Source

I sometime get this error in my logcat.

Failed to read row 0, column 0 from a CursorWindow which has 0 rows, 64 columns.

First, a bit of a back story. I have an app that runs on many devices in our organisation. Primarily, it currently runs on about 20 x Samsung Note 8 devices, 2 x Samsung Note 10.1 devices and a couple of other ones. So far, the problem has only been happening on 2 of the Note 8 devices. On all of the other devices it seems to work just fine.

How the app works is that users use the app to gather information, text, photos, signature etc... and all of this is then stored/inserted into the SQLite database as a row in the submissions table. The submissions table has 64 columns to cater for each field being collected. There is a sync method that is always running (it is an AsyncTask that is executed inside of a runnable thread, so even if the app is closed, it still syncs the data in the background, unless you swipe close it from the android task manager), that syncs the data to a remote server and that checks each 10 seconds if a sync is needed, for example if a new submission was inserted, it will then start syncing that submission. When a submission is finished syncing with the server and receives a success response, it is then deleted from the device's database. So far it has been working great and thousands of submissions have been synced successfully etc. however, every now and again, I get this one error from one or two specific Note 8 tablets that reproduce this problem. I have tried many times to re create the error but it always works when I test it and I have tried every type of scenario to test it.

My code is thousands of lines, so I will try to keep it as relevant as possible. First, here is the relevant code for the runnable:

public Runnable myRunnable = new Runnable()
 {

    @Override
    public void run()
    { 
       count ++;
                if(count >= 10)
                {
                    android.util.Log.w("     SYNC     ", "---------------");
                    android.util.Log.w("     SYNC     ", "Checking if sync method is busy");
                    if(!syncBusy)
                    {
                        android.util.Log.w("     SYNC     ", "Sync method OPEN");
                        doSync();//This will start doing sync.
                        count = 0;
                    }
                    else
                    {
                    android.util.Log.w("     SYNC     ", "Sync method BUSY, will try again in 10 seconds");
                    android.util.Log.w("     SYNC     ", "---------------");
                    }
                }
                if(count == 1 && !syncBusy)
                {
                    checkSubmissionsLefttoSync();
                }
       mHandler.postDelayed(myRunnable, 1000);
    }
};

Then, the doSync() method is where I do the upload and also where I get the error. It's over a thousands lines long, so I don't really want to post the code here. What I can say is that it works 100% for all devices except the one or two exceptions that produce the above mentioned error.

Also, I will post the part where I actually traverse the database inside of the sync method:

        databaseHelper = new Handler_Database(this);
        Cursor cursor2 = databaseHelper.getAllUnsyncedSubmissions();

        if(cursor2.getCount() > 0) 
        {
            if(cursor2.moveToFirst())
            {

              do
                {
                    try
                    {
                       //doing lookups here and populating sync arrays
                    }
                    catch (IllegalStateException e)
                    {
                        //Do Nothing
                    }
                    catch (NullPointerException e)
                    {
                        //Do Nothing
                    }
                }
                while(cursor2.moveToNext());

            }
            else
            {

            }
        }
        else
        {

        }
         cursor2.close();
         databaseHelper.close();

I have noticed something else and I am not sure if it's a problem, however, when I run the app, logcat outputs the following line about 6 or 7 times:

12:48:11.115 7416 #7416 DEBUG SQLiteOpenHelper DB version : 60

I have my own warning messages when the app starts up, and those ones only display once. However, the db version and other info is being written multiple times to the logcat. Is this a problem ? Does this mean my database has some sort of bug that it creates multiple instances ?

All the apps are updated with signed apk's from the playstore. My database onUpgrade method looks like this:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
    // Drop older table if existed
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_SUBMISSIONS);

    // Create tables again
    onCreate(db);
}

This works fine for all the other tablets and device. They all update automatically from the playstore and continue to work as they should. However, why is logcat outputting those lines multiple times ?

Could this be part of the problem why I am getting the error as mentioned in the beginning of this post ?

Any possible insight would be gladly appreciated. I have been pulling my hair out for weeks and cannot find any fault in my code.

UPDATE 1:

I just saw this warning:

08:30:58.710 16745 #16745 WARN CursorWindow Window is full: requested allocation 307333 bytes, free space 249426 bytes, window size 2097152 bytes

I guess that this might be the root of my problems. Any ideas on how to solve this efficiently ?

UPDATE 2:

I think a solution might be to limit the columns that I retrieve from with the cursor. E.g., all the small text values/columns. Then afterwards, create a new query for each of the columns that I know takes up too much space. Do you think this is a solution ? Anyone ?

UPDATE 3:

This might work, I think I will do the large fields in separate cursors after the initial one is recycled. Something like this (I just wrote some psuedo code):

Cursor c = db.rawQuery("SELECT Column1, Column2 .... FROM table " + ... , ...);

Solution:

See my solution here ! https://stackoverflow.com/a/26797130/1518916

Answer

Janpan picture Janpan · Nov 13, 2014

I finished my solution and it works fine now. Basically, the bottom line is that it is not ideal to store large data in a sqlite database on android. When working with images, rather store just the URI and keep the image on the device. HOWEVER, if you must, then I recommend to not load all image data into the same cursor, split them up and do multiple queries.

Please see my answer here https://stackoverflow.com/a/26797130/1518916