欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android- InputMethodManager Leaks Activity

程序员文章站 2022-04-19 17:36:25
...

InputMethodManager 内存泄漏问题

-What Happened

After putting in a new round of background images, I've noticed my app consistently crashing with out of memory exceptions if you exit and then restart the app.  I've tracked this down to the fact that these background images are never being garbage collected, because the entire activity is being kept in memory by the InputMethodManager.  

- Steps to reproduce the problem.
Create a new android project using Eclipse.  
Create a launcher activity.
Add a finalizer:
    protected void finalize()
    {
        Log.i("MainActivity", "Finalized!");
    }
Install and launch the activity
Back out of the activity, in ddms, force garbage collection. 
-- The log message will not activate.
Re-enter the activity, force garbage collection.
-- The log message will now print (for the first instance of the activity)

Exit the activity again.  Force garbage collection.  Dump the hprof profile.  Examine it in the Eclipse Memory Analyzer Tool plugin.  

You will see 
InputMethodManager -> PhoneWindows$DecorView -> <YourActivityName>

Related * posts I've found:

http://*.com/questions/5038158/main-activity-is-not-garbage-collected-after-destruction-because-it-is-reference

http://*.com/questions/5769748/inputmethodmanager-holds-reference-to-the-tabhost-memory-leak-oom-error

 

最后解决方案为

在onDestory中插入调用此方法

 private void fixInputMethodManager() {
        try{
            final Object imm = getSystemService(Context.INPUT_METHOD_SERVICE);

            final Reflector.TypedObject windowToken = new Reflector.TypedObject(getWindow().getDecorView().getWindowToken(), IBinder.class);

            Reflector.invokeMethodExceptionSafe(imm, "windowDismissed", windowToken);

            final Reflector.TypedObject view = new Reflector.TypedObject(null, View.class);

            Reflector.invokeMethodExceptionSafe(imm, "startGettingWindowFocus", view);

            Reflector.fixInputMethodManagerViewLeak(ChartActivity.this, (InputMethodManager) imm);
        }catch (Exception ignored){

        }

    }

添加一个inputmethod的处理类,通过反射进行

public class Reflector {

    private static final String TAG = "Reflector";

    public static final class TypedObject {
        private final Object object;
        private final Class type;

        public TypedObject(final Object object, final Class type) {
            this.object = object;
            this.type = type;
        }

        Object getObject() {
            return object;
        }

        Class getType() {
            return type;
        }
    }

    public static void invokeMethodExceptionSafe(final Object methodOwner, final String method, final TypedObject... arguments) {
        if (null == methodOwner) {
            return;
        }

        try {
            final Class<?>[] types = null == arguments ? new Class[0] : new Class[arguments.length];
            final Object[] objects = null == arguments ? new Object[0] : new Object[arguments.length];

            if (null != arguments) {
                for (int i = 0, limit = types.length; i < limit; i++) {
                    types[i] = arguments[i].getType();
                    objects[i] = arguments[i].getObject();
                }
            }

            final Method declaredMethod = methodOwner.getClass().getDeclaredMethod(method, types);

            declaredMethod.setAccessible(true);
            declaredMethod.invoke(methodOwner, objects);
        } catch (final Throwable ignored) {
        }
    }

    public static void fixInputMethodManagerViewLeak(Context destContext, InputMethodManager imm) {
        if (destContext == null) {
            return;
        }
        String[] arr = new String[]{"mCurRootView", "mServedView", "mNextServedView"};
        Field f = null;
        Object obj_get = null;
        for (String param : arr) {
            try {
                f = imm.getClass().getDeclaredField(param);
                if (!f.isAccessible()) {
                    f.setAccessible(true);
                }
                obj_get = f.get(imm);
                if (obj_get != null && obj_get instanceof View) {
                    View v_get = (View) obj_get;
                    if (v_get.getContext() == destContext) {  // referenced context is held InputMethodManager want to destroy targets
                        f.set(imm, null);  // set empty, destroyed node path to gc
                    } else {
                        // Not want to destroy the target, that is, again into another interface, do not deal with, to avoid affecting the original logic, there is nothing further for the cycle
                        Log.e(TAG, "fixInputMethodManagerLeak break, context is not suitable, get_context=" + v_get.getContext() + " dest_context=" + destContext);
                        break;
                    }
                }
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }
}