tags:

views:

299

answers:

5

It walks like a bug, it chirps like a bug.... I need someone to confirm that it's a bug.

I was trying to get phone numbers from the address book with this code:

public JSONObject getAllPhones() throws JSONException{ 
String mPhoneNumberProjection[] = new String[] {
        Contacts.Phones.NAME, Contacts.Phones.NUMBER, Contacts.Phones.TYPE
};
Uri phoneNumbersUri = Contacts.Phones.CONTENT_URI;
JSONObject result = new JSONObject(); 
JSONArray phones = new JSONArray();

Cursor myCursor = mApp.managedQuery(phoneNumbersUri, mPhoneNumberProjection, null, null, null);
mApp.startManagingCursor(myCursor);
if (!myCursor.isAfterLast()){
    myCursor.moveToFirst();
    do{
        JSONObject aRow = new JSONObject();
        for (int i = 0; i < myCursor.getColumnCount(); i++){
            Log.d(LOG_TAG, myCursor.getColumnName(i) + " -> " + myCursor.getString(i));
            aRow.put(myCursor.getColumnName(i), myCursor.getString(i));
        }
        phones.put(aRow);
    } while (myCursor.moveToNext());
}
result.put("phones", phones);
result.put("phoneTypes", getPhoneTypes());
return result;
}

But when there are no contacts, and !myCursor.isAfterLast() evaluates as "false", program steps into "for" loop. So it enters "if" block, skips several methods and lands in "for" loop...

When I extract this loop into a separate function, everything works ok.

I'm doing this in Eclipse Helios on 32-bit Vista. I tried cleaning the project, erasing Java cache, restarting computer, creating new AVD... I used Android 1.6 and Android 2.2, but the error stays on...

Can someone explain what's going on?

A: 

I agree, this smells like a compiler bug. It's possible that the compiler creates a wrong jump target address in the byte code. Do you have the same problem on the emulator and device? You could add an emulator for a different phone (and different hardware architecture) to check, if this is a general problem or if it is limited to your device.


What one could try - grab a decompiler (I'd recommend JD Java Decompiler, hoping it works for android applications too) and try to decompile the class files to see, id the decompiler result matches your source code - at least logically. If there's a big difference -> potential compiler bug -> issue report to the android development team.

Andreas_D
He tried on emulators with 1.6 and 2.2
ognian
@ognian - yes, but I guess with android 1.6 and 2.2 for the *same* device, A different device would require a different compiler and if the error is gone when compiling for a different device, then it most likely is a compiler bug.
Andreas_D
I've created several different devices - a few for Android 1.6 and another one for Android 2.2 (although I never tried it on a real device)
zorglub76
It'll be outstanding if you could share a sample Eclipse project that reproduces this bug. You'll get feedback from various environments, and if it turns out to be a real bug, it's going to be big news
ognian
Well, I can't share the whole project, but now I gave the problematic code to a colleague that works on the same project, and it passes ok. I don't get it. But anyways, it seems that the problem is somewhere on my machine...
zorglub76
That sort of thing is usually associated with uninitialized data or a race condition. The former doesn't usually apply to Java, since it guarantees that objects are zeroed out, though it's possible for stuff to "leak in" from an underlying native implementation.
fadden
A: 

Are you sure isAfterLast evaluates to false if the cursor is before the first of 0 entries? ;) Why don't you just evaluate moveToFirst() instead? It would evaluate to false if there are no entries so the call to isAfterLast() is unnecessary anyway.

hackbert
quote: *"and !myCursor.isAfterLast() evaluates as "false"* - I guess, he tested that already.
Andreas_D
yep - it's false
zorglub76
Yeah, I read that, but the only other possible explanation is that the if statement is broken. And come on, how likely is that? Besides, the code ignores if moveToFirst() returns false, which is a bad thing.
hackbert
That's exactly why I put "weird" in the description of this behavior :)
zorglub76
Drawn from your comment below: If moveToFirst() returns true your cursor is not empty! Therefore isAfterLast() evaluates to false. Just look at the outcome of getCount().
hackbert
Initially, I thought that compiled class and source that debugger was using are out of sync. That's why I cleaned the project, java cache, restarted pc and created fresh avd...
zorglub76
"If moveToFirst() returns true your cursor is not empty" - but why it works with the function instead of for loop?
zorglub76
I don't know how that changed code looks like, so I can't tell. If you just put the loop in a function it isn't any better. Why don't you use the code posted by Pentium10?
hackbert
Changed code looks like this: http://pastebin.com/xxNKuUEt Anyway, I know I can rewrite it however I want, but I'm curious why the original code didn't work. Can you paste that getAllPhones() function above to see if it'll work on your machine? I'd like to know if this is the bug in Android, or only some glitch on my system...
zorglub76
This code has the same problem for an empty cursor. There is no glitch.
hackbert
So, it's an Android's bug, after all?
zorglub76
No, the bug is in your code.
hackbert
@Andreas_D: Code to fix this was posted three hours ago by Pentium10. What's a SO answer btw?
hackbert
@hackbert - sorry, was pretty confused, my comment was useless and ridiculuos, I removed it right away :)
Andreas_D
@hackbert - the OP has a fix: extracting the for loop to a method. If you were right, then in case of an empty contact list and `isAfterLast()` evaluating to false we'd expect the extracted method to be called instead of the for loop - which doesn't seem to be the case.
Andreas_D
A: 

rewrite that if and while into this

try {
    if (c!=null) {
        for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
               //stuff here
        }
    }
} finally {
    if (c!=null) {
        c.close();
    }
}
Pentium10
As a workaround?
Andreas_D
ha! that's a cute loop.. but i've already created a functional workaround with extracting for loop into a function.
zorglub76
I don't get why he needs double check he easily could go with `if (myCursor.moveToFirst()) then`
Pentium10
actually, myCursor.moveToFirst() returned true for some reason, although I had no contacts. Initially I tried with if (myCursor.moveToFirst())...
zorglub76
+1  A: 

It seems that this behavior cannot be reproduced outside my machine, so, I guess, you are all safe from harm :)

zorglub76
And today it works normally on my machine... Damn you, gremlins...
zorglub76
A: 

I believe isAfterLast is working as intended. isAfterLast will be false because there are no rows as you stated, so it can never evaluate if it is at the last row.

Plus in your code example above you never moved to the first row to begin with prior to calling isAfterLast(), The if statement never gets evaluated again.

use

if (cursor.moveToFirst()) {
       // code
    while(cursor.isAfterLast() == false) {
        // code
     String[] columNames = cursor.getColumnNames();
     for (String column : columnNames)  {
         aRow.put(column, cursor.getString(cursor.getColumnIndex(column))); 
     }
    cursor.moveToNext();
    phones.put(aRow);

    }
}

Hope that helps.

userdelroot