【问题标题】:Memory leak with Firebase Android PhoneAuthProviderFirebase Android PhoneAuthProvider 的内存泄漏
【发布时间】:2019-07-02 13:28:31
【问题描述】:

Firebase -> PhoneAuthProvider -> VerifyPhoneNumber 正在泄漏。我相信,它可能是 OnVerificationStateChangedCallbacks,我们在呼叫时将其发送给 verifyPhoneNumber。

重现步骤:

  1. 启动应用程序
  2. 为基于电话的身份验证选择“PhoneAuthActivity”
  3. 发送电话号码。
  4. 点击返回。

点击返回时出现泄露的内存

有人有同样的问题吗?任何解决方案?

public void FirebasePhoneUser(String phoneNumber) {
            mCallback = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
                @Override
                public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
                    Log.d("Completed","");
                }
                @Override
                public void onVerificationFailed(FirebaseException e) {
                    Log.d("Error","");
                }

                @Override
                public void onCodeSent(String verificationId,
                                  PhoneAuthProvider.ForceResendingToken forceResendingToken) {
                    Log.d("onCodeSent", "");
                }
            };
            phoneAuthProvider = PhoneAuthProvider.getInstance();
            phoneAuthProvider.verifyPhoneNumber(
                    phoneNumber,
                    30,
                    TimeUnit.SECONDS,
                    TaskExecutors.MAIN_THREAD,
                    mCallback
            );
}

【问题讨论】:

  • 您是通过 LeakCanary 发现的,还是什么?我不知道解决方案,我只是想确保您已正确识别问题。
  • 是的,使用 LeakCanary
  • 如果您发现 Firebase SDK 存在明显问题,请向 Firebase 支持提交错误报告,以便他们尝试重现该问题并收集信息。 Stack Overflow 对此无能为力。 support.google.com/firebase/contact/support
  • 有一个类似的问题始于 2017 年,但没有解决方案。这就是我在这里写它的原因。
  • 不幸的是,这不是很清楚。请记住,仅仅因为在活动运行后内存保持分配状态并不意味着存在泄漏,因为由于调用验证电话号码,状态可能会发生变化(以及内存使用量的变化)。您认为泄漏的是哪种类型的内存?你觉得它是如何被泄露的?你认为应该怎么做才能消除泄漏?

标签: java android firebase-authentication


【解决方案1】:

鉴于 API 很糟糕并且没有退订选项,您有多种解决方法。

  1. 代理或装饰器。您创建另一个 OnVerificationStateChangedCallbacks 将方法调用委托给另一个实例:
// this class must be either top-level or 'static'!
public /*static*/ final class DelegatingVerificationStateCallbacks
    extends PhoneAuthProvider.OnVerificationStateChangedCallbacks
    implements Closeable {
    @Nullable private PhoneAuthProvider.OnVerificationStateChangedCallbacks delegate;
    public DelegatingVerificationStateCallbacks(
        @NonNull PhoneAuthProvider.OnVerificationStateChangedCallbacks delegate
    ) {
        this.delegate = delegate;
    }

    @Override public void onCodeSent(
        @NonNull String verificationId,
        @NonNull PhoneAuthProvider.ForceResendingToken forceResendingToken
    ) {
        if (delegate != null) delegate.onCodeSent(verificationId, forceResendingToken);
    }
    @Override public void onCodeAutoRetrievalTimeOut(@NonNull String s) {
        if (delegate != null) delegate.onCodeAutoRetrievalTimeOut(s);
    }
    @Override public void onVerificationCompleted(@NonNull PhoneAuthCredential phoneAuthCredential) {
        if (delegate != null) delegate.onVerificationCompleted(phoneAuthCredential);
    }
    @Override public void onVerificationFailed(@NonNull FirebaseException e) {
        if (delegate != null) delegate.onVerificationFailed(e);
    }

    @Override public void close() {
        delegate = null;
    }
}

我已经实现了 Closeable 进行清理,但您可以实现 RxJava 的 Disposable 或其他替代方式。

这里的使用模式是显而易见且众所周知的:

public final class SomeScreen extends ActivityOrFragmentOrControllerOrWhatever {
    private final ArrayList<Closeable> disposeBag = new ArrayList<>();
    private void performAuth() {
        DelegatingVerificationStateCallbacks callbacks =
            new DelegatingVerificationStateCallbacks(
                new OnVerificationStateChangedCallbacks() { … }
            );
        disposeBag.add(callbacks);
        phoneAuthProvider.verifyPhoneNumber(…, callbacks);
    }
    @Override protected void onDestroy() {
        for (Closeable c : disposeBag) {
            try { c.close(); }
            catch (IOException ignored) { }
        }
        disposeBag.clear();
    }
}

结果:Firebase 泄露了对空且廉价的 DelegatingVerificationStateCallbacks 的引用,而不是 Activity。

  1. 自己清空引用。您可以采用上面介绍的方法来清除您自己对 Activity 的引用。这意味着这些引用必须是明确的,即。 e.类不能是匿名的或在你的活动中是内部的。您必须完全控制类的构造函数和字段,顶级类或嵌套的static 类非常适合。

  2. 弱参考。这不太明确并且涉及一些间接但仍然有效:您实例化顶级或嵌套static 类,将 Activity 传递给构造函数,将其包装在 WeakReference 中,然后分配给字段。就是这样,一段时间后WeakReference#get 将开始返回null

  3. 反射。非常糟糕且不稳定的选项,在其他一些情况下可能会有所帮助。有时,您的 Activity 可能会被 Android SDK 或供应商特定代码泄露,并且上述选项不适用。然后你可以自己清空一些私有字段。不要为 Firebase 执行此操作。

【讨论】:

    猜你喜欢
    • 2011-12-31
    • 1970-01-01
    • 1970-01-01
    • 2012-11-14
    • 2014-08-05
    • 2015-07-20
    • 2013-08-23
    • 2020-08-15
    相关资源
    最近更新 更多