InputMethodManager导致的内存泄漏
2017-12-21 14:59
561 查看
最近在使用LeakCanary的时候在首屏的SplashActivity经常会遇到内存泄漏的情况 看了下报错信息指向了 InputMethodManager的方法。网上的解决方法很多,测试了几种,在这里记录一下以防又忘了。
Solution:在onDestroy()中添加以下方法
具体参考此链接 http://blog.csdn.net/sodino/article/details/321888091.第一种解决方法(实测好像没什么用)
:InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); try { InputMethodManager.class.getDeclaredMethod("windowDismissed", IBinder.class).invoke(imm, getWindow().getDecorView().getWindowToken()); } catch (Exception e){ e.printStackTrace(); }
2.第二种解决方法
/** * Fix for https://code.google.com/p/android/issues/detail?id=171190 . * * When a view that has focus gets detached, we wait for the main thread to be idle and then * check if the InputMethodManager is leaking a view. If yes, we tell it that the decor view got * focus, which is what happens if you press home and come back from recent apps. This replaces * the reference to the detached view with a reference to the decor view. * * Should be called from {@link Activity#onCreate(android.os.Bundle)} )}. */ public static void fixFocusedViewLeak(Application application) { // Don't know about other versions yet. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1|| Build.VERSION.SDK_INT > 23) { return; } final InputMethodManager inputMethodManager = (InputMethodManager) application.getSystemService(Context.INPUT_METHOD_SERVICE); final Field mServedViewField; final Field mHField; final Method finishInputLockedMethod; final Method focusInMethod; try { mServedViewField = InputMethodManager.class.getDeclaredField("mServedView"); mServedViewField.setAccessible(true); mHField = InputMethodManager.class.getDeclaredField("mServedView"); mHField.setAccessible(true); finishInputLockedMethod = InputMethodManager.class.getDeclaredMethod("finishInputLocked"); finishInputLockedMethod.setAccessible(true); focusInMethod = InputMethodManager.class.getDeclaredMethod("focusIn", View.class); focusInMethod.setAccessible(true); } catch (NoSuchMethodException | NoSuchFieldException unexpected) { Log.e("IMMLeaks", "Unexpected reflection exception", unexpected); return; } c56c application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { @Override public void onActivityDestroyed(Activity activity){ } @Override public void onActivityStarted(Activity activity){ } @Override public void onActivityResumed(Activity activity){ } @Override public void onActivityPaused(Activity activity){ } @Override public void onActivityStopped(Activity activity){ } @Override public void onActivitySaveInstanceState(Activity activity, Bundle bundle){ } @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { ReferenceCleaner cleaner = new ReferenceCleaner(inputMethodManager, mHField, mServedViewField, finishInputLockedMethod); View rootView = activity.getWindow().getDecorView().getRootView(); ViewTreeObserver viewTreeObserver = rootView.getViewTreeObserver(); viewTreeObserver.addOnGlobalFocusChangeListener(cleaner); } }); } static class ReferenceCleaner implements MessageQueue.IdleHandler, View.OnAttachStateChangeListener, ViewTreeObserver.OnGlobalFocusChangeListener { private final InputMethodManager inputMethodManager; private final Field mHField; private final Field mServedViewField; private final Method finishInputLockedMethod; ReferenceCleaner(InputMethodManager inputMethodManager, Field mHField, Field mServedViewField, Method finishInputLockedMethod) { this.inputMethodManager = inputMethodManager; this.mHField = mHField; this.mServedViewField = mServedViewField; this.finishInputLockedMethod = finishInputLockedMethod; } @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) { if (newFocus == null) { return; } if (oldFocus != null) { oldFocus.removeOnAttachStateChangeListener(this); } Looper.myQueue().removeIdleHandler(this); newFocus.addOnAttachStateChangeListener(this); } @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { v.removeOnAttachStateChangeListener(this); Looper.myQueue().removeIdleHandler(this); Looper.myQueue().addIdleHandler(this); } @Override public boolean queueIdle() { clearInputMethodManagerLeak(); return false; } private void clearInputMethodManagerLeak() { try { Object lock = mHField.get(inputMethodManager); // This is highly dependent on the InputMethodManager implementation. synchronized (lock) { View servedView = (View) mServedViewField.get(inputMethodManager); if (servedView != null) { boolean servedViewAttached = servedView.getWindowVisibility() != View.GONE; if (servedViewAttached) { // The view held by the IMM was replaced without a global focus change. Let's make // sure we get notified when that view detaches. // Avoid double registration. servedView.removeOnAttachStateChangeListener(this); servedView.addOnAttachStateChangeListener(this); } else { // servedView is not attached. InputMethodManager is being stupid! Activity activity = extractActivity(servedView.getContext()); if (activity == null || activity.getWindow() == null) { // Unlikely case. Let's finish the input anyways. finishInputLockedMethod.invoke(inputMethodManager); } else { View decorView = activity.getWindow().peekDecorView(); boolean windowAttached = decorView.getWindowVisibility() != View.GONE; if (!windowAttached) { finishInputLockedMethod.invoke(inputMethodManager); } else { decorView.requestFocusFromTouch(); } } } } } } catch (IllegalAccessException |InvocationTargetException unexpected) { Log.e("IMMLeaks", "Unexpected reflection exception", unexpected); } } private Activity extractActivity(Context context) { while (true) { if (context instanceof Application) { return null; } else if (context instanceof Activity) { return (Activity) context; } else if (context instanceof ContextWrapper) { Context baseContext = ((ContextWrapper) context).getBaseContext(); // Prevent Stack Overflow. if (baseContext == context) { return null; } context = baseContext; } else { return null; } } } }
3.第三种方法
public static void fixInputMethodManagerLeak(Context destContext) { if (destContext == null) { return; } InputMethodManager imm = (InputMethodManager) destContext.getSystemService(Context.INPUT_METHOD_SERVICE); if (imm == null) { return; } String [] arr = new String[]{"mCurRootView", "mServedView", "mNextServedView"}; Field f = null; Object obj_get = null; for (int i = 0;i < arr.length;i ++) { String param = arr[i]; try{ f = imm.getClass().getDeclaredField(param); if (f.isAccessible() == false) { f.setAccessible(true); } // author: sodino mail:sodino@qq.com obj_get = f.get(imm); if (obj_get != null && obj_get instanceof View) { View v_get = (View) obj_get; if (v_get.getContext() == destContext) { // 被InputMethodManager持有引用的context是想要目标销毁的 f.set(imm, null); // 置空,破坏掉path to gc节点 } else { // 不是想要目标销毁的,即为又进了另一层界面了,不要处理,避免影响原逻辑,也就不用继续for循环了 if (QLog.isColorLevel()) { QLog.d(ReflecterHelper.class.getSimpleName(), QLog.CLR, "fixInputMethodManagerLeak break, context is not suitable, get_context=" + v_get.getContext()+" dest_context=" + destContext); } break; } } }catch(Throwable t){ t.printStackTrace(); } } }
相关文章推荐
- Android InputMethodManager 导致的内存泄露及解决方案
- InputMethodManager造成的内存泄漏问题及解决方法
- Android InputMethodManager 导致的内存泄露
- Android InputMethodManager 导致的内存泄露及解决方案
- Android InputMethodManager 导致的内存泄露及解决方案
- inputMethodManager造成的内存泄漏问题
- 处理 InputMethodManager 内存泄露的正确姿势
- 解决rxjava导致的内存泄漏
- 软键盘学习InputMethodManager
- InputMethodManagerService处理输入法——监听APK变动
- ffmpeg 内av_frame_move_ref 误用导致的内存泄漏
- InputMethodManager键盘控制
- static关键字所导致的内存泄漏问题
- 使用InputMethodManager 退出键盘最灵活的方式
- Android软键盘学习InputMethodManager
- Android软键盘学习InputMethodManager
- 使用block的时候,导致的内存泄漏
- InputMethodManager的用法,键盘,隐藏
- InputMethodManager的用法,键盘,隐藏
- Android 3.0 r1中文API文档(103) —— InputMethodManager [输入法]