CommonsWare 的回答解决了 Activity 内存泄漏的第一个原因,并且对追查第二个原因很有帮助。
第二个原因是 FingerprintManager 持有对 FingerprintManager.mAuthenticationCallback 中回调对象的强引用,并且在另一个 authenticate() 调用提供不同的回调对象之前不会释放它。
这是a known issue,截至 2018 年 12 月 17 日,他们尚未修复。
我的解决方法 (kludge) 是使用在应用程序上下文中创建的空回调对象再次调用 authenticate(),然后立即在空回调对象上调用 onAuthenticationFailed()。
它很乱,我肯定会投票给一个更好、更优雅的解决方案。
在某处声明一个静态变量(在本例中,在名为App 的类中)来保存空回调对象。
public static FingerprintManager.AuthenticationCallback EmptyAuthenticationCallback;
如果合适,在应用程序子类的onCreate() 中实例化它。
请注意,这需要 API 23+,因此请确保您的应用在较低的 API 中不会尝试使用它。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
App.EmptyAuthenticationCallback = new FingerprintManager.AuthenticationCallback() {};
}
在 FingerprintManager.AuthenticationCallback() 匿名对象中添加一个 clearCallbackReference() 方法。
private void clearCallbackReference() {
final String methodName = "clearCallbackReference()";
// FingerprintManager holds a strong reference to the callback
// which in turn holds a strong reference to the Activity
// and thus causes the Activity to be leaked.
// This is a known bug in the FingerprintManager class.
// http://code.google.com/p/android/issues/detail?id=215512
// And the CancellationSignal object does not clear the callback reference either.
//
// To clear it we call authenticate() again and give it a new callback
// (created in the application context instead of the Activity context),
// and then immediately "fail" the authenticate() call
// since we aren't wanting another fingerprint from the user.
try {
Log.d(TAG, methodName);
fingerprintManager.authenticate(null, null, 0, App.EmptyAuthenticationCallback, null);
App.EmptyAuthenticationCallback.onAuthenticationFailed();
}
catch (Exception ex) {
// Handle the exception..
}
}
修改FingerprintManager.AuthenticationCallback() 中的onAuthenticationSucceeded() 和onAuthenticationError() 方法以调用clearCallbackReference()。
例子:
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
final String methodName = "onAuthenticationSucceeded()";
try {
Log.d(TAG, methodName + ": Authentication succeeded for Action '" + action + "'.");
super.onAuthenticationSucceeded(result);
// Do your custom actions here if needed.
}
catch (Exception ex) {
// Handle the exception..
}
finally {
clearCallbackReference();
}
}
在onAuthenticationError() 中,我的 finally 块看起来像这样,因为有时 errMsgId 5 "Fingerprint operation canceled." 是一个虚假错误。
它通常在authenticate() 调用之后立即触发,但它并没有真正取消操作。
finally {
if (errMsgId != 5 || (canceler != null && canceler.isCanceled()))
clearCallbackReference();
}
canceler 是 CancellationSignal 对象,作为参数传入。