内存泄漏,但是如何传递与 activity 不同的上下文来解决泄漏?
Memory Leak, but how can I pass a different context than the one of the activity to solve the Leak?
LeakCanary
检测到以下泄漏
出现的地方:
GC ROOT android.hardware.fingerprint.FingerprintManager.this[=12=]
(android.hardware.fingerprint.IFingerprintServiceReceiver$Stub 的匿名子类)
参考文献 android.hardware.fingerprint.FingerprintManager.mContext
泄漏 com.alga.com.mohammed.views PasscodeActivity
实例
尝试替换:
val fingerprintManagerInstance = this.getSystemService(FINGERPRINT_SERVICE) ?: return
与:
val fingerprintManagerInstance = applicationContext.getSystemService(FINGERPRINT_SERVICE) ?: return
看看你是否能得到更好的结果。
CommonsWare 的回答解决了 Activity 内存泄漏的第一个原因,并且对追踪第二个原因很有帮助。
第二个原因是 FingerprintManager 在 FingerprintManager.mAuthenticationCallback
中持有对回调对象的强引用,并且在另一个 authenticate()
调用提供不同的回调对象之前不会释放它。
这是 a known issue,截至 2018 年 12 月 17 日,他们尚未修复。
我的解决方法(kludge)是使用在应用程序上下文中创建的空回调对象进行另一个 authenticate()
调用,然后立即对该空回调对象调用 onAuthenticationFailed()
。
它很乱,我肯定会投票选出一个更好、更优雅的解决方案。
在某处声明一个静态变量(在本例中名为 App
的 class 中)以保存空回调对象。
public static FingerprintManager.AuthenticationCallback EmptyAuthenticationCallback;
如果合适,在应用程序 subclass 的 onCreate()
中实例化它。
请注意,这需要 API 23+,因此请确保您的应用在 APIs 时不会尝试使用它。
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 对象,作为参数传入。
LeakCanary
检测到以下泄漏
出现的地方:
GC ROOT android.hardware.fingerprint.FingerprintManager.this[=12=]
(android.hardware.fingerprint.IFingerprintServiceReceiver$Stub 的匿名子类)
参考文献 android.hardware.fingerprint.FingerprintManager.mContext
泄漏 com.alga.com.mohammed.views PasscodeActivity
实例
尝试替换:
val fingerprintManagerInstance = this.getSystemService(FINGERPRINT_SERVICE) ?: return
与:
val fingerprintManagerInstance = applicationContext.getSystemService(FINGERPRINT_SERVICE) ?: return
看看你是否能得到更好的结果。
CommonsWare 的回答解决了 Activity 内存泄漏的第一个原因,并且对追踪第二个原因很有帮助。
第二个原因是 FingerprintManager 在 FingerprintManager.mAuthenticationCallback
中持有对回调对象的强引用,并且在另一个 authenticate()
调用提供不同的回调对象之前不会释放它。
这是 a known issue,截至 2018 年 12 月 17 日,他们尚未修复。
我的解决方法(kludge)是使用在应用程序上下文中创建的空回调对象进行另一个 authenticate()
调用,然后立即对该空回调对象调用 onAuthenticationFailed()
。
它很乱,我肯定会投票选出一个更好、更优雅的解决方案。
在某处声明一个静态变量(在本例中名为 App
的 class 中)以保存空回调对象。
public static FingerprintManager.AuthenticationCallback EmptyAuthenticationCallback;
如果合适,在应用程序 subclass 的 onCreate()
中实例化它。
请注意,这需要 API 23+,因此请确保您的应用在 APIs 时不会尝试使用它。
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 对象,作为参数传入。