Bug 25324 - Memory leaks with TextView inner class ChangeWatcher
Summary: Memory leaks with TextView inner class ChangeWatcher
Status: RESOLVED UPSTREAM
Alias: None
Product: Android
Classification: Xamarin
Component: BCL Class Libraries ()
Version: 4.20.0
Hardware: PC Mac OS
: --- normal
Target Milestone: ---
Assignee: Jonathan Pryor
URL:
Depends on:
Blocks:
 
Reported: 2014-12-12 07:13 UTC by Songtive Team
Modified: 2014-12-18 12:52 UTC (History)
2 users (show)

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

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:
Status:
RESOLVED UPSTREAM

Description Songtive Team 2014-12-12 07:13:42 UTC
The problem relies to "TextView" and it’s non-static inner class ‘ChangeWatcher’ which holds an implicit reference to it’s parent instance on Java side which in turn causes memory leaks when using "Spannable" strings as text values.

We can see this in MAT respectively:
http://i.gyazo.com/a0b97958f35a678fe65e09d4616209f7.png

The possible workaround is based on conditions under which "ChangeWatcher" instances are added in "TextView.java" method:

private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) {
…
if (text instanceof Spannable && !mAllowTransformationLengthChange) {
            Spannable sp = (Spannable) text;

            // Remove any ChangeWatchers that might have come from other TextViews.
            final ChangeWatcher[] watchers = sp.getSpans(0, sp.length(), ChangeWatcher.class);
            final int count = watchers.length;
            for (int i = 0; i < count; i++) {
                sp.removeSpan(watchers[i]);
            }

            if (mChangeWatcher == null) mChangeWatcher = new ChangeWatcher();
…
}

It directly relies to the value of private member "mAllowTransformationLengthChange", which should be set to "true" in appropriate method:

public final void setTransformationMethod(TransformationMethod method) {
…
	if (method instanceof TransformationMethod2) {
            TransformationMethod2 method2 = (TransformationMethod2) method;
            mAllowTransformationLengthChange = !isTextSelectable() && !(mText instanceof Editable);
            method2.setLengthChangesAllowed(mAllowTransformationLengthChange);
        } else {
            mAllowTransformationLengthChange = false;
        }
…
}

As we can see when text is not selectable and ‘mText’ is not an instance of Editable (SpannableString is suitable), we can set it to "true" via "TransformationMethod" property of "TextView" meets the conditions.

The major problem is that a ‘method’ argument must be an instance of public interface implementation:
android.text.method.TransformationMethod2 which doesn't exist in Xamarin.Android framework.

This is the last possible solution among other described on StackOverflow which didn’t help us:
http://stackoverflow.com/questions/10237908/textview-un-removed-changewatchers-cause-memory-leak
Comment 1 Songtive Team 2014-12-16 11:41:12 UTC
Found another bug memory leak report on StackOverflow, I think it's pretty critical one:
http://stackoverflow.com/questions/18348049/android-edittext-memory-leak
Comment 2 Jonathan Pryor 2014-12-18 12:52:28 UTC
I'm not entirely sure what you're asking for here. Xamarin.Android binds the Android types; if there are bugs in the Android code that would be exposed to Java apps, those same bugs will *also* be present in C# apps using Xamarin.Android. We don't and can't fix underlying ("upstream") Android bugs.

> The major problem is that a ‘method’ argument must be an instance of publicinterface implementation:
> android.text.method.TransformationMethod2 which doesn't exist in Xamarin.Android framework.

The reason that android.text.method.TransformationMethod2 doesn't exist in Xamarin.Android is because it doesn't exist in the Android SDK either; the type has @hide applied, which prevents it from being exposed in android.jar:

https://github.com/android/platform_frameworks_base/blob/7f8f79a1ff086c04a3ad2a442b1d39a8186e3e50/core/java/android/text/method/TransformationMethod2.java#L22

Consequently, it does not exist in the public SDK documentation:

# This is a 404
http://developer.android.com/reference/android/text/method/TransformationMethod2.html

If you check android.jar, it doesn't exist there either:

$ jar tf $ANDROID_SDK_PATH/platforms/android-21/android.jar | grep TransformationMethod2

There is nothing for Xamarin.Android to do here. We can't bind types which don't exist in android.jar; attempts to do so will simply result in javac compilation errors.