android inputmethodmanager内存泄露,Android InputMethodManager 导致的内存泄露及解决方案...

package ai.botbrain.ttcloud.sdk.util;

import android.annotation.TargetApi;

import android.app.Activity;

import android.app.Application;

import android.content.Context;

import android.content.ContextWrapper;

import android.os.Build;

import android.os.Bundle;

import android.os.Looper;

import android.os.MessageQueue;

import android.util.Log;

import android.view.View;

import android.view.ViewTreeObserver;

import android.view.inputmethod.InputMethodManager;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

/**

* Description:

* Creator: Created by peter.

* Date: 17/4/24.

*/

public class InputMethodMemoryUtil {

/**

* 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)} )}.

*/

@TargetApi(Build.VERSION_CODES.KITKAT)

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;

}

@TargetApi(Build.VERSION_CODES.KITKAT)

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;

}

}

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值