在android 11的 InputMethodManager的源码中,查看 windowDismissed(),如下
public void windowDismissed(IBinder appWindowToken) {
// Intentionally empty.
//
// It seems that some applications call this method via reflection to null clear the
// following fields that used to exist in InputMethodManager:
// * InputMethodManager#mCurRootView
// * InputMethodManager#mServedView
// * InputMethodManager#mNextServedView
// so that these objects can be garbage-collected when an Activity gets dismissed.
//
// It is indeed true that older versions of InputMethodManager had issues that prevented
// these fields from being null-cleared when it should have been, but the understanding of
// the engineering team is that all known issues have already been fixed as of Android 10.
//
// For older devices, developers can work around the object leaks by using
// androidx.activity.ComponentActivity.
// See https://issuetracker.google.com/u/1/issues/37122102 for details.
//
// If you believe InputMethodManager is leaking objects in API 24 or any later version,
// please file a bug at https://issuetracker.google.com/issues/new?component=192705.
}
解决方案:对于低于android10,且非 ComponentActivity的context,反射获取InputMethodManager对象中的 mCurRootView、mServedView、mNextServedView这三个属性,转换为View类型后,判断view的context等于要释放的Activity的context时,将这个属性置为null。
fun fixMemoryLeak(context: Context?) {
try {
context ?: return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) return // android 10 已修复
if (context is ComponentActivity) return // androidx.activity.ComponentActivity 已修复
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager ?: return
imm.javaClass.declaredFields.filter {
it.name == "mCurRootView" || it.name == "mServedView" || it.name == "mNextServedView"
}.forEach { filed ->
val origin = filed.isAccessible
if (!origin) {
filed.isAccessible = true
}
(filed.get(imm) as? View)?.takeIf { it.context == context }?.also {
filed.set(imm, null)
}
filed.isAccessible = origin
}
} catch (e: Exception) {
e.printStackTrace()
}
}



