我通过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对象,作为参数传入。

08-05 19:29