今天偶然看看androidx.activity包中的类,发现了ImmLeaksCleaner,看来谷歌已经发现了InputMethodManager造成的内存泄漏问题,并在ComponentActivity中通过Lifecycle机制解决这个问题,但是由于国内定制机型比较多,还是会造成泄漏,下面来看看谷歌怎么做的。
先来看看那ImmLeaksCleaner的源码。
@RequiresApi(19)
final class ImmLeaksCleaner implements LifecycleEventObserver {
private static final int NOT_INITIALIAZED = 0;
private static final int INIT_SUCCESS = 1;
private static final int INIT_FAILED = 2;
private static int sReflectedFieldsInitialized = NOT_INITIALIAZED;
private static Field sHField;
private static Field sServedViewField;
private static Field sNextServedViewField;
private Activity mActivity;
ImmLeaksCleaner(Activity activity) {
mActivity = activity;
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
if (event != Lifecycle.Event.ON_DESTROY) {
return;
}
if (sReflectedFieldsInitialized == NOT_INITIALIAZED) {
initializeReflectiveFields();
}
if (sReflectedFieldsInitialized == INIT_SUCCESS) {
InputMethodManager inputMethodManager = (InputMethodManager)
mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
final Object lock;
try {
lock = sHField.get(inputMethodManager);
} catch (IllegalAccessException e) {
return;
}
if (lock == null) {
return;
}
synchronized (lock) {
final View servedView;
try {
servedView = (View) sServedViewField.get(inputMethodManager);
} catch (IllegalAccessException e) {
return;
} catch (ClassCastException e) {
return;
}
if (servedView == null) {
return;
}
if (servedView.isAttachedToWindow()) {
return;
}
// Here we have a detached mServedView. Set null to mNextServedViewField so that
// everything will be cleared in the next InputMethodManager#checkFocus().
try {
sNextServedViewField.set(inputMethodManager, null);
} catch (IllegalAccessException e) {
return;
}
}
// Assume that InputMethodManager#isActive() internally triggers
// InputMethodManager#checkFocus().
inputMethodManager.isActive();
}
}
@MainThread
private static void initializeReflectiveFields() {
try {
sReflectedFieldsInitialized = INIT_FAILED;
sServedViewField = InputMethodManager.class.getDeclaredField("mServedView");
sServedViewField.setAccessible(true);
sNextServedViewField = InputMethodManager.class.getDeclaredField("mNextServedView");
sNextServedViewField.setAccessible(true);
sHField = InputMethodManager.class.getDeclaredField("mH");
sHField.setAccessible(true);
sReflectedFieldsInitialized = INIT_SUCCESS;
} catch (NoSuchFieldException e) {
// very oem much custom ¯\_(ツ)_/¯
}
}
}
实际上谷歌的解决方案和网上很多方案的思路也是一样的都是通过反射将泄露的引用设置为null,因为InputMethodManager造成泄漏的原因是,mCurRootView, mServedView, mNextServedView持有的context造成activity销毁但是没有被回收造成的内存泄露,我前面提到由于国内定制机型很多,比如华为还有mLastSrvView也会造成内存泄露,所以谷歌的解决方案只是针对原生系统。
ImmLeaksCleaner 实现LifecycleEventObserver有实现了LifecycleObserver,就是通过Activity的生命周期对这些在成内存泄漏的View设置为null。
接着在ComponentActivity中注册ImmLeaksCleaner 监听Activity的生命周期,代码如下:
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
.................................
if (19 <= SDK_INT && SDK_INT <= 23) {
getLifecycle().addObserver(new ImmLeaksCleaner(this));
}
}
可以看到当19 <= SDK_INT && SDK_INT <= 23是才会做这些处理
再来来看看LifecycleEventObserver源码
public interface LifecycleEventObserver extends LifecycleObserver {
/**
* Called when a state transition event happens.
*
* @param source The source of the event
* @param event The event
*/
void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}
实际上LifecycleEventObserver 就是个接口定义了onStateChanged方法,当Activity的声明周期被触发就会毁掉这个方法。前面说到谷歌的解决方案并不能解决国内的情况,所以我们可以在这个基础上做修改,代码我也就贴出来了,需要注意的是谷歌的这种方案是针对。