Re: [android-developers] Activity lifecycle and a null cursor

2012-05-09 Thread Raffaele Sgarro
I don't know if my assumptions are right, anyway I pushed an update that
seems to fix the bug (at least, I have zero reports since I put the new
version online, whereas I used to have a few dozens). Here is what I did
currently

   1. Removed android:onClick in my XML files
   2. Manually set the listener inside my custom adapter bindView()
   3. Avoid leaks: since the listener happens to be an activity, I unset it
   either onPause() (via AbsListView.reclaimViews(List) and via a custom
   AbsListView.RecycleListener

I want to remark, I don't know if my assumptions are correct and I don't
know it this solves the bug. However if both of these are true, we have a
collision between View and Activity lifecycle. Basically, if I am right,
the View is reused in another activity (since its original one is paused)
but still has the old one set as its listener. This causes the NPE because
the old activity cursor is effectively null. Again, note that I don't have
a single proof of these assumptions: I think the whole thing would be
indeed very dangerous, but is the only coherent and conceptually right
explanation. The problem here is that I've never been able to reproduce the
bug, so I must base my assumptions on the crash reports (which are stopped
after the upgrade).

I'd like some Android dev on the list to tell if this is indeed possible,
because it would be a dangerous thing (it could also be the case for memory
leaks). I read the sources myself, but didn't find the relevant code. There
is AbsListView.RecycleBin, but it's an instance field of the ListView and I
don't think that the whole list view is reused among different Activities
in this case.

Related question on StackOverflow

2012/5/7 Jason Teagle 

>   Unfortunately the crash reports don't show the device model. The NPE
> happens when the user clicks a row or one of the buttons in a row (since
> all three actions share the same code).
>
> I have imagined a possible cause for this issue. Looking into the sources
> (android/view/View.java) here is what the constructor does when it founds
> android:onClick in XML: an anonymous listener is created, and this
> listener will call the method on the Context returned by View.getContext(),
> but this context may be a different instance than the one currently holding
> the view. This is because 
> Adapter.getView()gives
>  a chance for reusing an already existing View, and the default
> strategy for CursorAdapter (as seen in the sources) is to reuse that simply
> if it's not null. So here is my scenario
>
>1. Instance#1 of MyActivity is created, its listview is set up and
>shown. Instance#1 is set as the listener for the list_item view, as per
>android:onClick in my XML layout
>2. onPause is called on instance#1, which causes my method to set
>adapter's cursor to null
>3. the system decides that a new instance of MyActivity is needed, so
>instance#2 is created. A new cursor is also created and its contents are
>shown in the list view (via bindView()), but the list item views are
>recycled somehow, since they were kept in a cache. The old views still keep
>a reference to instance#1, so when the click event is triggered,
>instance#1.onClick() is fired and it founds a null cursor, thus throwing
>the NPE
>
> This explanation requires a little bit of imagination, but I think it's
> still realistic, even if all is to be demonstrated. I am writing a
> separated, minimal test case to dive into the Android classes with the
> debugger, but it's not simple. I use an activity with a list view as the
> only content view, and a list_item with an android:onClick property defined
> in XML. My goal is to see if a new instance of the activity can be created
> even when another one is already available. From the Android doc, this
> would be perfectly legitimate, but is not easy to trigger. The second step
> is to click the list item and compare the list_item's context with the
> current Activity. If they are different, we have caught it.
>
> Does anybody have any suggestion or experience on this?
>
> @Jason, as per the tap tempo, the algorithm to detect the BPM requires
> three taps at enough close intervals, and the resulting value must also be
> in the constraints. If you try to tap while you listen a song, it will do
> its job
>
> @Blake, I can argue it from the stack trace. The NPE is throws at this line
> int position = cursor.getPosition();
> so the cursor is obviously null, and the only code that change the cursor
> (and the adapter) is onPause(), where I set adapter.changeCursor(null);
>
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "Android Developers" group.
> To post to this group, send email to android-developers@googlegroups.com
> To unsubscribe from this group, s

Re: [android-developers] Activity lifecycle and a null cursor

2012-05-07 Thread Jason Teagle
Unfortunately the crash reports don't show the device model. The NPE happens 
when the user clicks a row or one of the buttons in a row (since all three 
actions share the same code). 

I have imagined a possible cause for this issue. Looking into the sources 
(android/view/View.java) here is what the constructor does when it founds 
android:onClick in XML: an anonymous listener is created, and this listener 
will call the method on the Context returned by View.getContext(), but this 
context may be a different instance than the one currently holding the view. 
This is because Adapter.getView() gives a chance for reusing an already 
existing View, and the default strategy for CursorAdapter (as seen in the 
sources) is to reuse that simply if it's not null. So here is my scenario
  1.. Instance#1 of MyActivity is created, its listview is set up and shown. 
Instance#1 is set as the listener for the list_item view, as per 
android:onClick in my XML layout 
  2.. onPause is called on instance#1, which causes my method to set adapter's 
cursor to null 
  3.. the system decides that a new instance of MyActivity is needed, so 
instance#2 is created. A new cursor is also created and its contents are shown 
in the list view (via bindView()), but the list item views are recycled 
somehow, since they were kept in a cache. The old views still keep a reference 
to instance#1, so when the click event is triggered, instance#1.onClick() is 
fired and it founds a null cursor, thus throwing the NPE 
This explanation requires a little bit of imagination, but I think it's still 
realistic, even if all is to be demonstrated. I am writing a separated, minimal 
test case to dive into the Android classes with the debugger, but it's not 
simple. I use an activity with a list view as the only content view, and a 
list_item with an android:onClick property defined in XML. My goal is to see if 
a new instance of the activity can be created even when another one is already 
available. From the Android doc, this would be perfectly legitimate, but is not 
easy to trigger. The second step is to click the list item and compare the 
list_item's context with the current Activity. If they are different, we have 
caught it.

Does anybody have any suggestion or experience on this?

@Jason, as per the tap tempo, the algorithm to detect the BPM requires three 
taps at enough close intervals, and the resulting value must also be in the 
constraints. If you try to tap while you listen a song, it will do its job

@Blake, I can argue it from the stack trace. The NPE is throws at this line
int position = cursor.getPosition();
so the cursor is obviously null, and the only code that change the cursor (and 
the adapter) is onPause(), where I set adapter.changeCursor(null);

-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Re: [android-developers] Activity lifecycle and a null cursor

2012-05-07 Thread Raffaele Sgarro
Indeed I agree. I am preparing a dialog to give users an opportunity to
submit more information than a typical crash report.

However I hope that someone from the Android team is on the list and can
put some light on the overall Activity lifecycle and the management of
views, threads and instance variables, because there is a total lack of
documentation on these things. Sure, we have the sources, but it'd be very
sad if they were the most accurate technical specification

2012/5/7 Jason Teagle 

> while debugging on my devices (and emulators) I never got this NPE, so
>> basically
>> I am blind and must try to figure out what could happen on other devices.
>> But, without being able to reproduce this behavior, the local debug is
>> useless. I'd
>> need to get in touch with the users
>>
>
> I don't envy you in that difficult situation. If you can enlist the help
> of several helpful users to help you track it down by adding temporary
> debug info into the code and letting them use the temporary version of the
> app, it will be of benefit. It's in their interest as much as yours to get
> the situation resolved.
>
> Are you aware of any devices in particular that cause this problem?
>
>
>
>  Triple checking everything is not the way to make robust software.
>>
> ...
>
>> with unneeded checkings,
>>
>
> Be careful about this philosophy. In your code sample, for example, you
> have
>
>
> Cursor cursor = adapter.getCursor();
> int position = cursor.getPosition();
>
> You are using 'cursor' here without checking it for null - you have *zero*
> checks in there. Whilst I appreciate that 'triple checking' was a slightly
> exaggerated comment, having *no* checks can be just as bad.
>
>
>
>  because if I don't catch the bug it will pop up sooner or later
>>
>
> Agreed. But having your users have to put up with constant failures
> because you prefer leaving things to fail so that you catch them rather
> than coding defensively (and *log* the issue in a catch handler, for
> example, so that you still get information on the problem somehow) isn't
> always an ideal situation. Some users will prefer to have it fail rather
> than do something odd, as per your philosophy, but others will prefer to
> have it try its best to move on rather than die, die, die. I'm willing to
> bet that the majority fall into the latter category.
>
>
>
>  Android seems to me too much an unreliable platform. i don't know if it's
>> for
>> missing documentation, bad design or whatever, just every piece of the API
>> fails at some point
>>
>
> Although I do feel that the documentation has lots of room for extra
> information, I would suggest that more of the problem you state is to do
> with trying to get one platform (Android) to cope with all the nuances of
> each manufacturer's (and even each model's) hardware and firmware. It's not
> as if manufacturers code with the OS in mind (except, you'd hope, Google's
> own devices!).
>
>
>
>  Can a click event be fired before onResume() finishes executing?
>>
> ...
>
>  because it'd completely break the usefulness of Activity lifecycle
>> callbacks
>>
>
> Totally agreed. I would be alarmed if it were possible, to be honest.
>
>
>
>  cursor and populate the listview, and since the listview is not empty
>> (otherwise one
>> could not click an item and thus firing the event listeners), how can we
>> have two
>> different adapters?
>>
>
> I agree that it seems ludicrous, but bear in mind that you are (in the
> click handler) referring to a variable you are maintaining manually
> (adapter), rather than getting the adapter directly from the list view.
> There is also the possibility of a threading issue, but since everything
> happens in the same activity I assume this should not be an issue - it
> should all be happening on the main UI thread.
>
> I can only suggest using
>
> (SimpleCursorAdapter)listView.**getAdapter().getCursor()
>
> rather than using the [adapter] member variable and see what happens.
>
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "Android Developers" group.
> To post to this group, send email to 
> android-developers@**googlegroups.com
> To unsubscribe from this group, send email to
> android-developers+**unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/**group/android-developers?hl=en
>

-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Re: [android-developers] Activity lifecycle and a null cursor

2012-05-07 Thread Jason Teagle
while debugging on my devices (and emulators) I never got this NPE, so 
basically

I am blind and must try to figure out what could happen on other devices.
But, without being able to reproduce this behavior, the local debug is 
useless. I'd

need to get in touch with the users


I don't envy you in that difficult situation. If you can enlist the help of 
several helpful users to help you track it down by adding temporary debug 
info into the code and letting them use the temporary version of the app, it 
will be of benefit. It's in their interest as much as yours to get the 
situation resolved.


Are you aware of any devices in particular that cause this problem?



Triple checking everything is not the way to make robust software.

...

with unneeded checkings,


Be careful about this philosophy. In your code sample, for example, you have

Cursor cursor = adapter.getCursor();
int position = cursor.getPosition();

You are using 'cursor' here without checking it for null - you have *zero* 
checks in there. Whilst I appreciate that 'triple checking' was a slightly 
exaggerated comment, having *no* checks can be just as bad.




because if I don't catch the bug it will pop up sooner or later


Agreed. But having your users have to put up with constant failures because 
you prefer leaving things to fail so that you catch them rather than coding 
defensively (and *log* the issue in a catch handler, for example, so that 
you still get information on the problem somehow) isn't always an ideal 
situation. Some users will prefer to have it fail rather than do something 
odd, as per your philosophy, but others will prefer to have it try its best 
to move on rather than die, die, die. I'm willing to bet that the majority 
fall into the latter category.



Android seems to me too much an unreliable platform. i don't know if it's 
for

missing documentation, bad design or whatever, just every piece of the API
fails at some point


Although I do feel that the documentation has lots of room for extra 
information, I would suggest that more of the problem you state is to do 
with trying to get one platform (Android) to cope with all the nuances of 
each manufacturer's (and even each model's) hardware and firmware. It's not 
as if manufacturers code with the OS in mind (except, you'd hope, Google's 
own devices!).




Can a click event be fired before onResume() finishes executing?

...
because it'd completely break the usefulness of Activity lifecycle 
callbacks


Totally agreed. I would be alarmed if it were possible, to be honest.


cursor and populate the listview, and since the listview is not empty 
(otherwise one
could not click an item and thus firing the event listeners), how can we 
have two

different adapters?


I agree that it seems ludicrous, but bear in mind that you are (in the click 
handler) referring to a variable you are maintaining manually (adapter), 
rather than getting the adapter directly from the list view. There is also 
the possibility of a threading issue, but since everything happens in the 
same activity I assume this should not be an issue - it should all be 
happening on the main UI thread.


I can only suggest using

(SimpleCursorAdapter)listView.getAdapter().getCursor()

rather than using the [adapter] member variable and see what happens.


--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en


Re: [android-developers] Activity lifecycle and a null cursor

2012-05-07 Thread Raffaele Sgarro
I appreciate your pragmatic response, however

   - while debugging on my devices (and emulators) I never got this NPE, so
   basically I am blind and must try to figure out what could happen on other
   devices. But, without being able to reproduce this behavior, the local
   debug is useless. I'd need to get in touch with the users
   - Triple checking everything is not the way to make robust software.
   Deeply understanding the API is. I don't want to mask my bugs with unneeded
   checkings, because if I don't catch the bug it will pop up sooner or later
   - Android seems to me too much an *unreliable* platform. i don't know if
   it's for missing documentation, bad design or whatever, just every piece of
   the API fails at some point

You introduced two interesting questions (which I was already asking myself
since a couple of days):

   1. Can a click event be fired before onResume() finishes executing?
   Sure, I could set and clear a flag, but I'd like to see some documentation
   on this, because it'd completely break the usefulness of Activity lifecycle
   callbacks
   2. Does my code call the very same adapter?

A little bit of more information can help answer these questions. In my
activity I let Android automatically register/unregistering observers by
using android:onClick in the XML layout. The adapter is recreated
onResume() and is stored as an instance field of my activity (which also is
the OnClickListener). Since the adapter is used to read from the cursor and
populate the listview, and since the listview is not empty (otherwise one
could not click an item and thus firing the event listeners), how can we
have two different adapters?

If you want to try to crash my app this is the link to download
it.
The Activity we are talking about is started when the user clicks on the
LCD screen of the metronome

2012/5/7 Jason Teagle 

> adapter = new SimpleCursorAdapter( /* query the SQLite db */);
>>
>> I can't understand why cursor may be null, since onResume() it's clearly
>> initialized by
>> the query.
>>
>
> You would expect so, but it couldn't hurt to check (as a debugging test)
> if adapter.getCursor() actually returns a valid cursor immediately after
> the call to new SimpleCursorAdapter() above.
>
> It might also be worth checking that the adapter instance you set into the
> list view in onResume() and the one you are subsequently seeing in
> onClickItem() are one and the same. Again, you'd expect it to be, but at
> this point you've got little to lose by checking.
>
> Just as a sanity check, make sure onResume() *is* being called as you
> expect, before onClickItem() fires. Stranger things have happened...
>
>
>
>  it's clearly initialized by the query
>>
> ...
>
>  since obviously it must be non-null
>>
>
> If programming (and running into problems) can teach you one thing,
> hopefully it will be that making assumptions is a mistake {:v)  There is
> nothing in the docs for the SimpleCursorAdapter constructor that says it
> *guarantees* a valid cursor after the operation, even though that is the
> logical expectation.
>
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "Android Developers" group.
> To post to this group, send email to 
> android-developers@**googlegroups.com
> To unsubscribe from this group, send email to
> android-developers+**unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/**group/android-developers?hl=en
>

-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Re: [android-developers] Activity lifecycle and a null cursor

2012-05-07 Thread Jason Teagle

adapter = new SimpleCursorAdapter( /* query the SQLite db */);

I can't understand why cursor may be null, since onResume() it's clearly 
initialized by

the query.


You would expect so, but it couldn't hurt to check (as a debugging test) if 
adapter.getCursor() actually returns a valid cursor immediately after the 
call to new SimpleCursorAdapter() above.


It might also be worth checking that the adapter instance you set into the 
list view in onResume() and the one you are subsequently seeing in 
onClickItem() are one and the same. Again, you'd expect it to be, but at 
this point you've got little to lose by checking.


Just as a sanity check, make sure onResume() *is* being called as you 
expect, before onClickItem() fires. Stranger things have happened...




it's clearly initialized by the query

...

since obviously it must be non-null


If programming (and running into problems) can teach you one thing, 
hopefully it will be that making assumptions is a mistake {:v)  There is 
nothing in the docs for the SimpleCursorAdapter constructor that says it 
*guarantees* a valid cursor after the operation, even though that is the 
logical expectation.



--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en


[android-developers] Activity lifecycle and a null cursor

2012-05-07 Thread Raffaele Sgarro
My activity displays a ListView which retrieves its data from the 
associated cursor. I close the database (and the cursor) onPause, and get a 
new database (and a new cursor) onResume. This means that events callback 
should access the Cursor only when it's not null. However I get lots of NPE 
in my listeners. This is the pseudo code (I don't post the whole activity 
because it's 200 lines and is not runnable without also having XML files, 
strings and drawables)

onResume() {
  super.onResume();
  database = new Database(this).getWriteableDatabase();
  adapter = new SimpleCursorAdapter( /* query the SQLite db */);
  listView.setAdapter(adapter);
}

onPause() {
  super.onPause();
  adapter.changeCursor(null); // this closes the cursor
  database.close();
}

onClickItem(View view) {
  Cursor cursor = adapter.getCursor();
  int position = cursor.getPosition(); // This line throws the NPE
}


I can't understand why cursor may be null, since onResume() it's clearly 
initialized by the query. I can't reproduce this on my devices, which makes 
it really hard to debug. Also, the situation is quite unrecoverable, 
meaning that I would consider it a programming error to check inside the 
event callback if cursor is null, since obviously it must be non-null. If 
it is, it's better to let my app fail, so I eventually fix this.

-- 
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en