Утечка памяти, но как я могу передать контекст, отличный от того, который используется для устранения утечки? - PullRequest
0 голосов
/ 26 ноября 2018

У меня есть следующая утечка, обнаруженная 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 экземпляр

Ответы [ 2 ]

0 голосов
/ 18 декабря 2018

Ответ CommonsWare устраняет первую причину утечки памяти в Activity и оказал большую помощь в поиске второй.

Вторая причина заключается в том, что FingerprintManager содержит строгую ссылку на объект обратного вызова в FingerprintManager.mAuthenticationCallback и не освобождает его до тех пор, пока другой вызов authenticate() не предоставит другой объект обратного вызова.

Это известная проблема , которую они еще не исправили по состоянию на 17 декабря 2018 г..

Мой обходной путь (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, передаваемый в качестве параметра.

0 голосов
/ 11 декабря 2018

Попробуйте заменить:

val fingerprintManagerInstance = this.getSystemService(FINGERPRINT_SERVICE) ?: return

на:

val fingerprintManagerInstance = applicationContext.getSystemService(FINGERPRINT_SERVICE) ?: return

и посмотрите, получите ли вы лучшие результаты.

...