我通过LeakCanary
检测到以下泄漏
出现的地方:
GC ROOT android.hardware.fingerprint。 FingerprintManager$1.this$0
(android.hardware.fingerprint.IFingerprintServiceReceiver $ Stub的匿名子类)
引用android.hardware.fingerprint。 FingerprintManager.mContext
泄漏com.alga.com.mohammed.views PasscodeActivity
实例
最佳答案
CommonsWare的答案解决了Activity内存泄漏的第一个原因,并且对于追踪第二个原因很有帮助。
第二个原因是FingerprintManager在FingerprintManager.mAuthenticationCallback
中拥有对回调对象的强大引用,并且直到另一个authenticate()
调用提供了不同的回调对象后才释放它。
截至2018年12月17日,这是a known issue,他们尚未修复。
我的解决方法(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..
}
}
修改
onAuthenticationSucceeded()
中的onAuthenticationError()
和FingerprintManager.AuthenticationCallback()
方法以调用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对象,作为参数传入。