美文网首页Android技术Android开发
Android InputMethodManager内存泄漏

Android InputMethodManager内存泄漏

作者: 云飞扬1 | 来源:发表于2018-08-30 16:54 被阅读89次

    最近通过LeakCanary检测内存泄漏,发现一个很莫名其妙的问题,截图如下所示:


    内存泄漏

    从这里可以看到,InputMethodManager的mNextServedView持有了一个RecyclerView的引用,而RecyclerView则持有了IndexActivity的引用,从而导致了IndexActivity的内存泄漏。

    据说这是输入法的一个bug,在LeakCanary上有个提到过这个问题(https://github.com/square/leakcanary/issues/572),但是该问题并不是一个普遍性的bug,而是部分机型上会出现。本人拿手头上的几台设备测试过,小米MIX2、小米红米5、三星Note3、三星Galaxy S7 edge、Vivo X9、Oppo R9还有一台华为设备忘记型号了,只有三星 Galaxy S7 edge上频繁出现InputMethodManager内存泄漏的问题。不得不说,谷歌的Android系统坑真是多,各种机型、各种分辨率,但是没办法,有坑我们也得一个一个去填。

    查看InputMethodManager的源码可以看到以下定义:

        /**
         * This is the root view of the overall window that currently has input
         * method focus.
         */
        View mCurRootView;
        /**
         * This is the view that should currently be served by an input method,
         * regardless of the state of setting that up.
         */
        View mServedView;
        /**
         * This is then next view that will be served by the input method, when
         * we get around to updating things.
         */
        View mNextServedView;
    

    通过LeakCanary的检测发现,发生内存泄漏时,这三者都有可能被InputMethodManager持有依赖引用,而View则一直被Activity持有引用,进而导致了Activity的内存泄漏。
    那么怎么解决这个问题呢?参考了不少方案,发现还是采用反射来将这些依赖的View置空,手动切断对象的引用链最有效,具体代码如下:

    public class MemoryLeakUtil {
    
        public static void fixInputMethodMemoryLeak(Context context) {
            if (context == null)
                return;
            InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
            if (inputMethodManager == null)
                return;
    
            String[] viewArr = new String[]{"mCurRootView", "mServedView", "mNextServedView"};
            Field field;
            Object fieldObj;
            for (String view : viewArr) {
                try {
                    field = inputMethodManager.getClass().getDeclaredField(view);
                    if (!field.isAccessible()) {
                        field.setAccessible(true);
                    }
                    fieldObj = field.get(inputMethodManager);
                    if (fieldObj != null && fieldObj instanceof View) {
                        View fieldView = (View) fieldObj;
                        if (fieldView.getContext() == context) {
                            //注意需要判断View关联的Context是不是当前Activity,否则有可能造成正常的输入框输入失效
                            field.set(inputMethodManager, null);
                        } else {
                            break;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    

    在内存泄漏的Activity里增加:

        @Override
        protected void onDestroy() {
            //手动切断InputMethodManager里的View引用链
            MemoryLeakUtil.fixInputMethodMemoryLeak(this);
            super.onDestroy();
        }
    

    相关文章

      网友评论

        本文标题:Android InputMethodManager内存泄漏

        本文链接:https://www.haomeiwen.com/subject/wdcrwftx.html