Firebase Android PhoneAuthProvider存在内存泄漏问题。

3
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。 - Nicolas Guasch
1
有一个类似的问题在2017年被提出,但没有解决方案。这就是为什么我在这里写的原因。 - Nicolas Guasch
1
很不幸,这并不是非常清楚的。请记住,仅仅因为活动运行后内存保持分配并不意味着存在泄漏,因为由于验证电话号码的调用,状态(和内存使用)可能会发生变化。您认为正在泄漏哪种类型的内存?您认为它是如何泄漏的?您认为应该采取什么措施来消除泄漏? - Frank van Puffelen
好的,我开始通过停用Leak Canary并使用Android Studio Profiler工具(显示应用程序的内存消耗)运行应用程序。我重新创建了发生内存泄漏的事件,多次发送手机并返回到上一个活动,虽然内存逐渐增加,但我无法确定它是否仅来自verifyphonenumber(Firebase)。 - Nicolas Guasch
我遇到了同样的问题。 - Rowan Gontier
显示剩余3条评论
1个回答

2

鉴于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. 手动将引用设为null。您可以采取上面介绍的方法来清除对Activity的引用。这意味着这些引用必须是显式的,即类不能是匿名的或内部的。您必须完全控制类构造函数和字段,顶级类或嵌套的static类是一个很好的选择。

  2. 弱引用。这种方法不太明显,涉及一些间接性,但仍然有效:您实例化顶级或嵌套的static类,将Activity传递给构造函数,将其包装在WeakReference中,并分配给一个字段。就这样,经过一段时间后,WeakReference#get将开始返回null

  3. 反射。这是非常糟糕和不稳定的选项,在某些其他情况下可能有所帮助。有时Android SDK或供应商特定代码可能会泄漏您的Activity,上述选项不适用。然后,您可以自己将某些私有字段设置为null。请勿针对Firebase执行此操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接