/** * 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;    }    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;            }        }    }}

更多相关文章

  1. 代码中设置drawableleft
  2. android 3.0 隐藏 系统标题栏
  3. Android开发中activity切换动画的实现
  4. Android(安卓)学习 笔记_05. 文件下载
  5. Android中直播视频技术探究之—摄像头Camera视频源数据采集解析
  6. 技术博客汇总
  7. android 2.3 wifi (一)
  8. AndRoid Notification的清空和修改
  9. Android中的Chronometer

随机推荐

  1. 配置android app 方法数超过65k问题
  2. 在android tegra2中添加一个camera
  3. Android Installation error: INSTALL_FA
  4. TextView的一些属性
  5. Android P应用显示宽高比maxAspectRatio
  6. Android调试神器stetho使用详解和改造
  7. 【Android Developer Blog】Android Stud
  8. Android高手进阶教程(五)之----Android
  9. android 组件
  10. android音乐播放器开发_开篇