Fixed
Status Update
Comments
py...@gmail.com <py...@gmail.com> #2
Note: I have reproduced this on API 19, 21 and 22.
A fix could be to replace hard references with weak references for the following fields: "mServedView", "mNextServedView" and "mServedInputConnection" (the served input connection has a reference to the served view).
A fix could be to replace hard references with weak references for the following fields: "mServedView", "mNextServedView" and "mServedInputConnection" (the served input connection has a reference to the served view).
py...@gmail.com <py...@gmail.com> #3
There was a potential NPE in my sample, so here's an updated version. I also added an example of "fix" (hack??) which consists in calling requestFocusFromTouch() on the root view. This changes mServedView to point to the button that's on the screen. Meh.
#################
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.reflect.Field;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.widget.LinearLayout.VERTICAL;
public class MainActivity extends Activity {
private TextView immView;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(VERTICAL);
immView = new TextView(this);
final EditText leaking = new EditText(this);
Button button = new Button(this);
button.setText("Remove EditText");
button.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
layout.removeView(leaking);
layout.removeView(v);
Button fixLeak = new Button(MainActivity.this);
fixLeak.setText("Fix IMM leak");
fixLeak.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
View rootView = v.getRootView();
// This will give focus to the button.
rootView.requestFocusFromTouch();
}
});
layout.addView(fixLeak, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
}
});
layout.addView(immView, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
layout.addView(button, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
layout.addView(leaking, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
setContentView(layout);
updateImmView();
}
private void updateImmView() {
logServedView();
immView.postDelayed(new Runnable() {
@Override public void run() {
updateImmView();
}
}, 100);
}
private void logServedView() {
try {
Field sInstanceField = InputMethodManager.class.getDeclaredField("sInstance");
sInstanceField.setAccessible(true);
Object imm = sInstanceField.get(null);
if (imm == null) {
// Not set yet.
return;
}
Field mServedViewField = InputMethodManager.class.getDeclaredField("mServedView");
mServedViewField.setAccessible(true);
View servedView = (View) mServedViewField.get(imm);
if (servedView != null) {
immView.setText("InputMethodManager.mServedView: "
+ servedView.getClass().getName()
+ "\nAttached:"
+ servedView.isAttachedToWindow());
} else {
immView.setText("InputMethodManager.mServedView: null");
}
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
#################
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.lang.reflect.Field;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.widget.LinearLayout.VERTICAL;
public class MainActivity extends Activity {
private TextView immView;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(VERTICAL);
immView = new TextView(this);
final EditText leaking = new EditText(this);
Button button = new Button(this);
button.setText("Remove EditText");
button.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
layout.removeView(leaking);
layout.removeView(v);
Button fixLeak = new Button(MainActivity.this);
fixLeak.setText("Fix IMM leak");
fixLeak.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
View rootView = v.getRootView();
// This will give focus to the button.
rootView.requestFocusFromTouch();
}
});
layout.addView(fixLeak, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
}
});
layout.addView(immView, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
layout.addView(button, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
layout.addView(leaking, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
setContentView(layout);
updateImmView();
}
private void updateImmView() {
logServedView();
immView.postDelayed(new Runnable() {
@Override public void run() {
updateImmView();
}
}, 100);
}
private void logServedView() {
try {
Field sInstanceField = InputMethodManager.class.getDeclaredField("sInstance");
sInstanceField.setAccessible(true);
Object imm = sInstanceField.get(null);
if (imm == null) {
// Not set yet.
return;
}
Field mServedViewField = InputMethodManager.class.getDeclaredField("mServedView");
mServedViewField.setAccessible(true);
View servedView = (View) mServedViewField.get(imm);
if (servedView != null) {
immView.setText("InputMethodManager.mServedView: "
+ servedView.getClass().getName()
+ "\nAttached:"
+ servedView.isAttachedToWindow());
} else {
immView.setText("InputMethodManager.mServedView: null");
}
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
al...@android.com <al...@android.com> #4
Thank you for reporting this issue.
al...@android.com <al...@android.com>
py...@gmail.com <py...@gmail.com> #5
Thanks for looking into it.
Hopefully this will get fixed in AOSP.
In the meantime, app developers who want to fix this memory leak on older Android versions might go with this fix:https://gist.github.com/pyricau/4df64341cc978a7de414 . General idea is to check the IMM when a focused view gets detached, and force it let that reference go if it hasn't.
Hopefully this will get fixed in AOSP.
In the meantime, app developers who want to fix this memory leak on older Android versions might go with this fix:
yu...@google.com <yu...@google.com> #6
Thank you for reporting this issue. Android M Developer Preview 2 (MPZ79M) should contain the fix of this issue. Please let me know if this issue is still reproducible with MPZ79M.
Also sorry for not being able to fix this directly in AOSP. It was a bit technically hard to get this fixed in AOSP without causing bunch of merge conflicts with M branch. I'll keep this bug open until those commits become available in AOSP.
Also sorry for not being able to fix this directly in AOSP. It was a bit technically hard to get this fixed in AOSP without causing bunch of merge conflicts with M branch. I'll keep this bug open until those commits become available in AOSP.
yu...@google.com <yu...@google.com> #7
Here are the list of fixes I made for this issue.
-https://android.googlesource.com/platform/frameworks/base/+/97c381304207013fd95c7806df4dcca0c69006c0
-https://android.googlesource.com/platform/frameworks/base/+/5f05965f546fa42750f1a8314f8a2da01fd6bfb4
-https://android.googlesource.com/platform/frameworks/base/+/0f3a99d837048ae82855c2306cd454426393c19f
-https://android.googlesource.com/platform/frameworks/base/+/41bb4953dad33b56b6d039d5bc87a574a0b70fe9
-https://android.googlesource.com/platform/frameworks/base/+/0b52ed02dfc8c61e1a999d9c236f64503f7919d1
-https://android.googlesource.com/platform/frameworks/base/+/b13f015ab519b3e553c03eba971ada89b472fbbc
------
Basically what I did to fix this issue (inhttps://android.googlesource.com/platform/frameworks/base/+/b13f015ab519b3e553c03eba971ada89b472fbbc ) was relying on View#dispatchDetachedFromWindow() to clear on-going text input session (if any) managed in InputMethodManager. Ideally this should have been done in InputMethodManager#focusOut(), which has been virtually disabled by the following "false &&" from almost the beginning though.
https://android.googlesource.com/platform/frameworks/base/+blame/14e139179be7daab6ed452105387a3922752c219/core/java/android/view/inputmethod/InputMethodManager.java#1318
/**
* Call this when a view loses focus.
* @hide
*/
public void focusOut(View view) {
synchronized (mH) {
if (DEBUG) Log.v(TAG, "focusOut: " + view
+ " mServedView=" + mServedView
+ " winFocus=" + view.hasWindowFocus());
if (mServedView != view) {
// The following code would auto-hide the IME if we end up
// with no more views with focus. This can happen, however,
// whenever we go into touch mode, so it ends up hiding
// at times when we don't really want it to. For now it
// seems better to just turn it all off.
if (false && view.hasWindowFocus()) {
mNextServedView = null;
scheduleCheckFocusLocked(view);
}
}
}
}
Actually in Android M release cycle I initially tried to get rid of above "false &&", but finally gave up because of the reasons written inhttps://android.googlesource.com/platform/frameworks/base/+/0b52ed02dfc8c61e1a999d9c236f64503f7919d1 . Then I narrowed down the scope and introduced @hide InputMethodManager#onViewDetachedFromWindow() by copying InputMethodManager#focusOut() so that we can clear on-going text input session at least when the view is being detached.
Thanks again for reporting this issue.
-
-
-
-
-
-
------
Basically what I did to fix this issue (in
/**
* Call this when a view loses focus.
* @hide
*/
public void focusOut(View view) {
synchronized (mH) {
if (DEBUG) Log.v(TAG, "focusOut: " + view
+ " mServedView=" + mServedView
+ " winFocus=" + view.hasWindowFocus());
if (mServedView != view) {
// The following code would auto-hide the IME if we end up
// with no more views with focus. This can happen, however,
// whenever we go into touch mode, so it ends up hiding
// at times when we don't really want it to. For now it
// seems better to just turn it all off.
if (false && view.hasWindowFocus()) {
mNextServedView = null;
scheduleCheckFocusLocked(view);
}
}
}
}
Actually in Android M release cycle I initially tried to get rid of above "false &&", but finally gave up because of the reasons written in
Thanks again for reporting this issue.
yu...@gmail.com <yu...@gmail.com> #8
+1 for yukawa@google.com for detailed reply.
This is how Google should respond to issues submitted by developers. This makes developers feel appreciated.
This is how Google should respond to issues submitted by developers. This makes developers feel appreciated.
an...@gmail.com <an...@gmail.com> #9
Hi there, I still reproduced this issue on Nexus 5 API 23 Marshmallow
LeakCanary: * com.example.android.uamp.ui.MusicPlayerActivity has leaked:
LeakCanary: * GC ROOT static android.view.inputmethod.InputMethodManager.sInstance
LeakCanary: * references android.view.inputmethod.InputMethodManager.mNextServedView
LeakCanary: * references android.support.v7.widget.RecyclerView.mContext
LeakCanary: * leaks com.example.android.uamp.ui.MusicPlayerActivity instance
LeakCanary: * com.example.android.uamp.ui.MusicPlayerActivity has leaked:
LeakCanary: * GC ROOT static android.view.inputmethod.InputMethodManager.sInstance
LeakCanary: * references android.view.inputmethod.InputMethodManager.mNextServedView
LeakCanary: * references android.support.v7.widget.RecyclerView.mContext
LeakCanary: * leaks com.example.android.uamp.ui.MusicPlayerActivity instance
yu...@google.com <yu...@google.com> #10
Re #9:
Yes, I'm coincidentally chasing a remaining leak scenario, which probably shares the same root cause with the case that you are observing. Sorry for the trouble. I've filed Issue 36949180 so that you can put star to receive updates on it.
Yes, I'm coincidentally chasing a remaining leak scenario, which probably shares the same root cause with the case that you are observing. Sorry for the trouble. I've filed
yu...@gmail.com <yu...@gmail.com> #12
@py.ricau hello Android 23 will happen InputMethodManager.mNextServedView leaks,how to solve it ?
is...@gmail.com <is...@gmail.com> #13
The same on Android emulator API 23:
D/LeakCanary: * EXCLUDED LEAK.
D/LeakCanary: * com.example.activity.FileManagerActivity has leaked:
D/LeakCanary: * GC ROOT static android.view.inputmethod.InputMethodManager.sInstance
D/LeakCanary: * references android.view.inputmethod.InputMethodManager.mNextServedView , matching exclusion field android.view.inputmethod.InputMethodManager#mNextServedView
D/LeakCanary: * references android.widget.LinearLayout.mContext
D/LeakCanary: * leaks com.exampleactivity.FileManagerActivity instance
D/LeakCanary: * EXCLUDED LEAK.
D/LeakCanary: * com.example.activity.FileManagerActivity has leaked:
D/LeakCanary: * GC ROOT static android.view.inputmethod.InputMethodManager.sInstance
D/LeakCanary: * references android.view.inputmethod.InputMethodManager.mNextServedView , matching exclusion field android.view.inputmethod.InputMethodManager#mNextServedView
D/LeakCanary: * references android.widget.LinearLayout.mContext
D/LeakCanary: * leaks com.exampleactivity.FileManagerActivity instance
[Deleted User] <[Deleted User]> #14
This still repros in API 25.
yu...@google.com <yu...@google.com> #15
Re comment #14 :
> This still repros in API 25.
Hmm, it might be a separate issue. Can you provide concrete steps to reproduce (and a minimum repro code if possible)?
> This still repros in API 25.
Hmm, it might be a separate issue. Can you provide concrete steps to reproduce (and a minimum repro code if possible)?
pa...@gmail.com <pa...@gmail.com> #16
The same on API 23
In com.pavelkrylov.dnevnik:0.2.0:2.
* EXCLUDED LEAK.
* com.pavelkrylov.dnevnik.groups.main.MainActivity has leaked:
* GC ROOT android.view.inputmethod.InputMethodManager$1.this$0 (anonymous subclass of com.android.internal.view.IInputMethodClient$Stub)
* references android.view.inputmethod.InputMethodManager.mNextServedView , matching exclusion field android.view.inputmethod.InputMethodManager#mNextServedView
* references android.support.v7.widget.RecyclerView.mContext
* leaks com.pavelkrylov.dnevnik.groups.main.MainActivity instance
* Retaining: 4,5 КБ.
* Reference Key: d155c06b-cf24-4176-8b88-7697220de179
* Device: Sony Sony D5803 D5803
* Android Version: 6.0.1 API: 23 LeakCanary: 1.5.1 1be44b3
* Durations: watch=5079ms, gc=152ms, heap dump=2521ms, analysis=34801ms
In com.pavelkrylov.dnevnik:0.2.0:2.
* EXCLUDED LEAK.
* com.pavelkrylov.dnevnik.groups.main.MainActivity has leaked:
* GC ROOT android.view.inputmethod.InputMethodManager$1.this$0 (anonymous subclass of com.android.internal.view.IInputMethodClient$Stub)
* references android.view.inputmethod.InputMethodManager.mNextServedView , matching exclusion field android.view.inputmethod.InputMethodManager#mNextServedView
* references android.support.v7.widget.RecyclerView.mContext
* leaks com.pavelkrylov.dnevnik.groups.main.MainActivity instance
* Retaining: 4,5 КБ.
* Reference Key: d155c06b-cf24-4176-8b88-7697220de179
* Device: Sony Sony D5803 D5803
* Android Version: 6.0.1 API: 23 LeakCanary: 1.5.1 1be44b3
* Durations: watch=5079ms, gc=152ms, heap dump=2521ms, analysis=34801ms
ma...@gmail.com <ma...@gmail.com> #17
Here is a solution, In method of activity onDestroy use fixInputMethod();
/**
* fixInputMethod
*
* @author androidmalin
* @param context Context
*/
public static void fixInputMethod(Context context) {
if (context == null) return;
InputMethodManager inputMethodManager = null;
try {
inputMethodManager = (InputMethodManager) context.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
} catch (Throwable th) {
th.printStackTrace();
}
if (inputMethodManager == null) return;
String[] strArr = new String[]{"mCurRootView", "mServedView", "mNextServedView"};
for (int i = 0; i < 3; i++) {
try {
Field declaredField = inputMethodManager.getClass().getDeclaredField(strArr[i]);
if (declaredField == null) continue;
if (!declaredField.isAccessible()) {
declaredField.setAccessible(true);
}
Object obj = declaredField.get(inputMethodManager);
if (obj == null || !(obj instanceof View)) continue;
View view = (View) obj;
if (view.getContext() == context) {
declaredField.set(inputMethodManager, null);
} else {
return;
}
} catch (Throwable th) {
th.printStackTrace();
}
}
}
/**
* fixInputMethod
*
* @author androidmalin
* @param context Context
*/
public static void fixInputMethod(Context context) {
if (context == null) return;
InputMethodManager inputMethodManager = null;
try {
inputMethodManager = (InputMethodManager) context.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
} catch (Throwable th) {
th.printStackTrace();
}
if (inputMethodManager == null) return;
String[] strArr = new String[]{"mCurRootView", "mServedView", "mNextServedView"};
for (int i = 0; i < 3; i++) {
try {
Field declaredField = inputMethodManager.getClass().getDeclaredField(strArr[i]);
if (declaredField == null) continue;
if (!declaredField.isAccessible()) {
declaredField.setAccessible(true);
}
Object obj = declaredField.get(inputMethodManager);
if (obj == null || !(obj instanceof View)) continue;
View view = (View) obj;
if (view.getContext() == context) {
declaredField.set(inputMethodManager, null);
} else {
return;
}
} catch (Throwable th) {
th.printStackTrace();
}
}
}
ol...@gmail.com <ol...@gmail.com> #18
Very good solution, thank you.
This is additional code For API 16 and AppCompatEditText :
if (viewContext == context) {
declaredField.set(inputMethodManager, null);
} else { // for 4.1.2 (c)
if (viewContext instanceof TintContextWrapper)
if (((TintContextWrapper) viewContext).getBaseContext() == context)
declaredField.set(inputMethodManager, null);
}
This is additional code For API 16 and AppCompatEditText :
if (viewContext == context) {
declaredField.set(inputMethodManager, null);
} else { // for 4.1.2 (c)
if (viewContext instanceof TintContextWrapper)
if (((TintContextWrapper) viewContext).getBaseContext() == context)
declaredField.set(inputMethodManager, null);
}
sm...@gmail.com <sm...@gmail.com> #19
I am still seeing this on 27 (O_MR1). Would you prefer a new ticket?
yu...@google.com <yu...@google.com> #20
> I am still seeing this on 27 (O_MR1). Would you prefer a new ticket?
Yes, please.
Yes, please.
lu...@gmail.com <lu...@gmail.com> #21
Leaking for us as well.
yu...@google.com <yu...@google.com> #22
> Leaking for us as well.
Please file a new issue. There is almost nothing I can help, without concrete steps to reproduce and full bugreport file.
Please file a new issue. There is almost nothing I can help, without concrete steps to reproduce and full bugreport file.
da...@gmail.com <da...@gmail.com> #23
I am still seeing the leak on 27.
* GC ROOT static android.view.inputmethod.InputMethodManager.sInstance
* references android.view.inputmethod.InputMethodManager.mNextServedView
* references android.widget.ListView.mOnItemClickListener
* references android.preference.PreferenceScreen.mContext
* leaks com.android.settings.SubSettings instance
* GC ROOT static android.view.inputmethod.InputMethodManager.sInstance
* references android.view.inputmethod.InputMethodManager.mNextServedView
* references android.widget.ListView.mOnItemClickListener
* references android.preference.PreferenceScreen.mContext
* leaks com.android.settings.SubSettings instance
yu...@google.com <yu...@google.com> #24
Re comment #23 :
Thank you for the report, but can you please file a new bug with concrete steps to reproduce? Otherwise I can't help you because I have no idea about how to reproduce the issue.
Thank you for the report, but can you please file a new bug with concrete steps to reproduce? Otherwise I can't help you because I have no idea about how to reproduce the issue.
yu...@google.com <yu...@google.com> #25
For those who are still seeing the memory leak in API 25 or later builds, please file a new bug by following the steps explained in https://source.android.com/setup/report-bugs
Thanks!
Thanks!
yu...@google.com <yu...@google.com> #26
As for bug component, please use component "Framework". You can use the following link to file InputMethodManager related bugs.
https://issuetracker.google.com/issues/new?component=192705
bo...@gmail.com <bo...@gmail.com> #27
//modify above code, it`s work for me.
public static void fixInputMethod(Context context) {
if (context == null) {
return;
}
InputMethodManager inputMethodManager = null;
try {
inputMethodManager = (InputMethodManager) context.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
} catch (Throwable th) {
th.printStackTrace();
}
if (inputMethodManager == null) {
return;
}
Field[] declaredFields = inputMethodManager.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
try {
if (!declaredField.isAccessible()) {
declaredField.setAccessible(true);
}
Object obj = declaredField.get(inputMethodManager);
if (obj == null || !(obj instanceof View)) {
continue;
}
declaredField.set(inputMethodManager, null);
} catch (Throwable th) {
th.printStackTrace();
}
}
}
public static void fixInputMethod(Context context) {
if (context == null) {
return;
}
InputMethodManager inputMethodManager = null;
try {
inputMethodManager = (InputMethodManager) context.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
} catch (Throwable th) {
th.printStackTrace();
}
if (inputMethodManager == null) {
return;
}
Field[] declaredFields = inputMethodManager.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
try {
if (!declaredField.isAccessible()) {
declaredField.setAccessible(true);
}
Object obj = declaredField.get(inputMethodManager);
if (obj == null || !(obj instanceof View)) {
continue;
}
declaredField.set(inputMethodManager, null);
} catch (Throwable th) {
th.printStackTrace();
}
}
}
bo...@gmail.com <bo...@gmail.com> #28
public static void fixInputMethod(Context context) {
if (context == null) {
return;
}
InputMethodManager inputMethodManager = null;
try {
inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
} catch (Throwable th) {
th.printStackTrace();
}
if (inputMethodManager == null) {
return;
}
Field[] declaredFields = inputMethodManager.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
try {
if (!declaredField.isAccessible()) {
declaredField.setAccessible(true);
}
Object obj = declaredField.get(inputMethodManager);
if (obj == null || !(obj instanceof View)) {
continue;
}
View view = (View) obj;
if (view.getContext() == context) {
declaredField.set(inputMethodManager, null);
} else {
return;
}
} catch (Throwable th) {
th.printStackTrace();
}
}
}
if (context == null) {
return;
}
InputMethodManager inputMethodManager = null;
try {
inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
} catch (Throwable th) {
th.printStackTrace();
}
if (inputMethodManager == null) {
return;
}
Field[] declaredFields = inputMethodManager.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
try {
if (!declaredField.isAccessible()) {
declaredField.setAccessible(true);
}
Object obj = declaredField.get(inputMethodManager);
if (obj == null || !(obj instanceof View)) {
continue;
}
View view = (View) obj;
if (view.getContext() == context) {
declaredField.set(inputMethodManager, null);
} else {
return;
}
} catch (Throwable th) {
th.printStackTrace();
}
}
}
to...@gmail.com <to...@gmail.com> #29
Same fix as above but in Kotlin
fun fixInputMethod(context: Context?) {
if (context == null) {
return
}
var inputMethodManager: InputMethodManager? = null
try {
inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
} catch (th: Throwable) {
th.printStackTrace()
}
if (inputMethodManager == null) {
return
}
val declaredFields = inputMethodManager.javaClass.declaredFields
for (declaredField in declaredFields) {
try {
if (!declaredField.isAccessible) {
declaredField.isAccessible = true
}
val obj = declaredField.get(inputMethodManager)
if (obj == null || obj !is View) {
continue
}
val view: View = obj as View
if (view.context === context) {
declaredField.set(inputMethodManager, null)
} else {
return
}
} catch (th: Throwable) {
th.printStackTrace()
}
}
}
fun fixInputMethod(context: Context?) {
if (context == null) {
return
}
var inputMethodManager: InputMethodManager? = null
try {
inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
} catch (th: Throwable) {
th.printStackTrace()
}
if (inputMethodManager == null) {
return
}
val declaredFields = inputMethodManager.javaClass.declaredFields
for (declaredField in declaredFields) {
try {
if (!declaredField.isAccessible) {
declaredField.isAccessible = true
}
val obj = declaredField.get(inputMethodManager)
if (obj == null || obj !is View) {
continue
}
val view: View = obj as View
if (view.context === context) {
declaredField.set(inputMethodManager, null)
} else {
return
}
} catch (th: Throwable) {
th.printStackTrace()
}
}
}
mr...@gmail.com <mr...@gmail.com> #30
Googlers, will you fix that? 3 years old bug.. It's not funny
mr...@gmail.com <mr...@gmail.com> #31
I'm seeing this leak in API 28
yu...@google.com <yu...@google.com> #32
> Googlers, will you fix that? 3 years old bug.. It's not funny
Absolutely yes if you can provide a reliable and concrete way to reproduce the issue.
For the record, I received reports that this issue is not fixed in comment #14 , comment #19 , comment #23 , and now comment #30 , but no one provided a way to reproduce the issue.
Absolutely yes if you can provide a reliable and concrete way to reproduce the issue.
For the record, I received reports that this issue is not fixed in
yu...@google.com <yu...@google.com> #33
> Googlers, will you fix that? 3 years old bug.. It's not funny
Note also that the original case, which was for sure reported 3 years ago, should be fixed in Android M and later releases in as I explained in the comment #7 .
Note also that the original case, which was for sure reported 3 years ago, should be fixed in Android M and later releases in as I explained in the
yu...@google.com <yu...@google.com> #34
> I'm seeing this leak in API 28
That's not good. Can you please file a new issue with a concrete steps to reproduce the issue athttps://issuetracker.google.com/issues/new?component=192705 ?
I'm happy to take a look.
That's not good. Can you please file a new issue with a concrete steps to reproduce the issue at
I'm happy to take a look.
mr...@gmail.com <mr...@gmail.com> #35
Steps to reproduce:
1. Open "Search" (bottom navigation bar)
2. Tap on search input (EditText). Keyboard will be opened with InputMethodManager help. Implicit link to acitivity(see attached image) is inited at this moment I guess.
3. Leave activity to get leak. Close app in my case due to app has only one activity.
4. When app is closed LeakCanary will analyze dump in background and report leak. It takes few minutes.
Code:https://github.com/satorym/fosomas/tree/master/app
Fix discution on github (is mentioned on attached image):
https://gist.github.com/pyricau/4df64341cc978a7de414
Issue link that is mentioned on attached image:
https://github.com/square/leakcanary/issues/1#issuecomment-100579429
P.S. Sory, stack trace from LeakCanary is not coppyable, so I made snapshot. Sory for my emotional previous message and thanks for fast reply. I hope this data will help to solve issue.
1. Open "Search" (bottom navigation bar)
2. Tap on search input (EditText). Keyboard will be opened with InputMethodManager help. Implicit link to acitivity(see attached image) is inited at this moment I guess.
3. Leave activity to get leak. Close app in my case due to app has only one activity.
4. When app is closed LeakCanary will analyze dump in background and report leak. It takes few minutes.
Code:
Fix discution on github (is mentioned on attached image):
Issue link that is mentioned on attached image:
P.S. Sory, stack trace from LeakCanary is not coppyable, so I made snapshot. Sory for my emotional previous message and thanks for fast reply. I hope this data will help to solve issue.
yu...@google.com <yu...@google.com> #37
Re comment #35 .
Thank you for providing additional information. As I commented in Bug 116361867 , the log was obtained from an API 21 device not API 28 device. If you are seeing this on API 21, it's unfortunately not surprising because my fixes mentioned in my comment #7 were included only on API 23 and later devices.
inputMethodManager. leak canary report.txt
> * Device: Xiaomi Xiaomi Mi 4i ferrari
> * Android Version: 5.0.2 API: 21 LeakCanary: 1.5.4 74837f0
In Bug 116361867 I actually tried but so far I haven't succeeded to reproduce the issue. Can you update Bug 116361867 if you can still reliable reproduce the issue on API 28 devices?
Thank you for providing additional information. As I commented in
inputMethodManager. leak canary report.txt
> * Device: Xiaomi Xiaomi Mi 4i ferrari
> * Android Version: 5.0.2 API: 21 LeakCanary: 1.5.4 74837f0
In
gu...@gmail.com <gu...@gmail.com> #38
#37
I see the issue in API 23 LG Nexus 5
To reproduce just checkouthttps://github.com/Guneetgstar/LeakEditText.git build and run and make sure you focus on EditText after Activity.onDestroy() you will get the leak.
I see the issue in API 23 LG Nexus 5
To reproduce just checkout
Description
Repro steps are very easy:
1) Setup a view hierarchy with only one focusable view
2) Request focus on the focusable view.
3) Remove that view (or one of its ancestors) from the view hierarchy.
InputMethodManager.mServedView keeps a reference to that last focused view, until another view get focus. This prevent the part of the view hierarchy that was detached from being garbage collected, and therefore creates a memory leak.
Below is a repro case. Just drop that activity in any project and start it. The TextView displays the reference in InputMethodManager.mServedView updated every 100ms. The button removes the EditText. After the EditText is removed, you'll notice it's still referenced by InputMethodManager.mServedView, although it's detached.
###############
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.lang.reflect.Field;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.widget.LinearLayout.VERTICAL;
public class MainActivity extends Activity {
private TextView immView;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(VERTICAL);
immView = new TextView(this);
final EditText leaking = new EditText(this);
Button button = new Button(this);
button.setText("Remove EditText");
button.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
layout.removeView(leaking);
layout.removeView(v);
}
});
layout.addView(immView, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
layout.addView(button, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
layout.addView(leaking, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
setContentView(layout);
updateImmView();
}
private void updateImmView() {
logServedView();
immView.postDelayed(new Runnable() {
@Override public void run() {
updateImmView();
}
}, 100);
}
private void logServedView() {
try {
Field sInstanceField = InputMethodManager.class.getDeclaredField("sInstance");
sInstanceField.setAccessible(true);
Object imm = sInstanceField.get(null);
if (imm == null) {
// Not set yet.
return;
}
Field mServedViewField = InputMethodManager.class.getDeclaredField("mServedView");
mServedViewField.setAccessible(true);
View servedView = (View) mServedViewField.get(imm);
immView.setText("InputMethodManager.mServedView: "
+ servedView.getClass().getName()
+ "\nAttached:"
+ servedView.isAttachedToWindow());
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}