Bug 7929 - Subclassing EditItem results in infinite invocations of "Enabled"
Summary: Subclassing EditItem results in infinite invocations of "Enabled"
Alias: None
Product: Android
Classification: Xamarin
Component: Mono runtime / AOT Compiler ()
Version: 4.2.x
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: ---
Assignee: Bugzilla
Depends on:
Reported: 2012-10-22 14:33 UTC by Scott
Modified: 2012-10-24 11:24 UTC (History)
2 users (show)

Is this bug a regression?: ---
Last known good build:

test case solution/program (493.39 KB, application/zip)
2012-10-22 14:33 UTC, Scott

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Scott 2012-10-22 14:33:35 UTC
Created attachment 2772 [details]
test case solution/program

The enclosed/attached zip file contains a test program in which a subclassed EditItem control has an infinite loop.
This is evidenced by the console output in which a static counter is incremented with each invocation of the "Enabled" method that is overridden.
Comment 1 Jonathan Pryor 2012-10-22 16:30:26 UTC
Can you elaborate on what the problem is?

What do you mean by "infinite loop"? If I alter your MyText.Enabled property to be:

>	public override bool Enabled {
>		get {
>			Activity1.count++;
>			Console.WriteLine ("in MyText.Enabled: "+Activity1.count);
>			Console.WriteLine ("# Managed Stack: {0}", new System.Diagnostics.StackTrace (true));
>			using (var loc = new Java.Lang.Error ("Java callstack:"))
>				Console.WriteLine ("# Java Stack: {0}", loc.ToString ());
>			return base.Enabled;
>		}
>		set {
>			base.Enabled = value;
>		}
>	}

i.e. log the managed & java stacks at invocation, I see:

> in MyText.Enabled: 51
> # Managed Stack:    at Android.Views.View.n_IsEnabled(IntPtr jnienv, IntPtr native__this)
>    at System.Object.aa37679e-5b1c-4bc9-b5d3-11eff883c886(IntPtr , IntPtr )
> # Java Stack: Java.Lang.Error: Exception of type 'Java.Lang.Error' was thrown.
>   --- End of managed exception stack trace ---
> java.lang.Error: Java callstack:
>   at experiment15.MyText.n_isEnabled(Native Method)
>   at experiment15.MyText.isEnabled(MyText.java:45)
>   at android.widget.TextView.isTextEditable(TextView.java:7443)
>   at android.widget.Editor.isCursorVisible(Editor.java:391)
>   at android.widget.TextView.getUpdatedHighlightPath(TextView.java:4703)
>   at android.widget.TextView.onDraw(TextView.java:4882)
>   at android.view.View.draw(View.java:13458)
>   at android.view.View.draw(View.java:13342)
>   at android.view.ViewGroup.drawChild(ViewGroup.java:2929)
>   at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)
>   at android.view.View.draw(View.java:13340)
>   at android.view.ViewGroup.drawChild(ViewGroup.java:2929)
>   at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)
>   at android.view.View.draw(View.java:13340)
>   at android.view.ViewGroup.drawChild(ViewGroup.java:2929)
>   at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)
>   at android.view.View.draw(View.java:13340)
>   at android.view.ViewGroup.drawChild(ViewGroup.java:2929)
>   at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)
>   at android.view.View.draw(View.java:13461)
>   at android.widget.FrameLayout.draw(FrameLayout.java:467)
>   at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2183)
>   at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2258)
>   at android.view.ViewRootImpl.draw(ViewRootImpl.java:2153)
>   at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2021)
>   at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1832)
>   at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
>   at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4214)
>   at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
>   at android.view.Choreographer.doCallbacks(Choreographer.java:555)
>   at android.view.Choreographer.doFrame(Choreographer.java:525)
>   at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
>   at android.os.Handler.handleCallback(Handler.java:615)
>   at android.os.Handler.dispatchMessage(Handler.java:92)
>   at android.os.Looper.loop(Looper.java:137)
>   at android.app.ActivityThread.main(ActivityThread.java:4745)
>   at java.lang.reflect.Method.invokeNative(Native Method)
>   at java.lang.reflect.Method.invoke(Method.java:511)
>   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
>   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
>   at dalvik.system.NativeStart.main(Native Method)

What I don't see is the classic "infinite recursion" case, in which the same method name is repeated over and over and over and...

What I do see is that the managed stack trace barely exists; it's just a "normal" Java->managed stack transition.

The Java stack trace is more interesting, but of particular note are all the "draw" methods, eventually rooted in Looper.loop(), which loops for the lifetime of the process.

In short, this looks By Design, particularly with things like:

> To avoid a UI that feels sluggish or stutters during playback, you must 
> ensure that your animations consistently run at 60 frames per second.

"60 frames per second" implies to me that the entire View hierarchy will be drawn 60 times per second, meaning you should expect your MyText.Enabled property to be invoked...60 times per second. It isn't in this case (probably because of logging overhead); it's instead being invoked 4 times/second.
Comment 2 Scott 2012-10-22 17:04:39 UTC
When I run the example app, I see lots of write statements in my output window:

in MyText.Enabled: 3
in MyText.Enabled: 4
in MyText.Enabled: 5
in MyText.Enabled: 6
in MyText.Enabled: 7
in MyText.Enabled: 8
in MyText.Enabled: 9
in MyText.Enabled: 10
in MyText.Enabled: 11
in MyText.Enabled: 12
in MyText.Enabled: 13
in MyText.Enabled: 14

And it keeps going and going.  This indicates the MyText.Enabled is called continuously - right?
Comment 3 Jonathan Pryor 2012-10-22 17:22:12 UTC
Yes, that means it's being called continuously. That does NOT mean that it's stuck in an infinite recursive loop from which StackOverflowException and/or a process abort can be expected. As per the analysis in Comment #1, it just means that Android is constantly calling you, just in case you change your mind (so that the UI can update accordingly).
Comment 4 Scott 2012-10-24 11:24:32 UTC
I see.  Sorry about that.  I didn't realize that Android worked this way.
You can probably close this ticket.