防止线程被垃圾回收,避免上下文泄漏。

4
我想自定义从AccountManager获取认证令牌的过程。 AccountManagergetAuthToken()getAuthTokenByFeatures()方法,但我想实现一个自定义的流程,包括在活动之间切换等...
我希望按照以下方式实现:
public AccountManagerFuture<Bundle> getAuthTokenForActiveAccount() {
    GetAuthTokenForActiveAccountFuture future =
            new GetAuthTokenForActiveAccountFuture(MyActivity.this);
    future.start();
    return future;
}

在我的Activity中使用以下嵌套类:
  private static class GetAuthTokenForActiveAccountFuture extends Thread implements
                AccountManagerFuture<Bundle> {

    private final Activity mActivity;

    public GetAuthTokenForActiveAccountFuture(Activity activity) {
        mActivity = activity;
        // TODO: write this method
    }

    @Override
    public void run() {
         // TODO: write this method
    }

    @Override
    public boolean cancel(boolean b) {
        // TODO: write this method
        return false;
    }

    @Override
    public boolean isCancelled() {
        // TODO: write this method
        return false;
    }

    @Override
    public boolean isDone() {
        // TODO: write this method
        return false;
    }

    @Override
    public Bundle getResult() throws
            OperationCanceledException, IOException, AuthenticatorException {
        return internalGetResult(null, null);
    }

    @Override
    public Bundle getResult(long timeout, TimeUnit timeUnit) throws
            OperationCanceledException, IOException, AuthenticatorException {
        return internalGetResult(timeout, timeUnit);
    }

    private Bundle internalGetResult(Long timeout, TimeUnit timeUnit) throws
            OperationCanceledException, IOException, AuthenticatorException {
        // TODO: write this method
        return null;
    }
}

我的想法是我可以创建自己的AccountManagerFuture对象,并在完成所有必要步骤(其中一些包括活动切换)后才“解除阻止”其getResult()方法。

这里有两个问题:

  1. 我需要Activity上下文来在需要时切换到其他活动,但是我传递给构造函数的Activity应该在我切换到其他活动时被销毁,但由于我的Thread持有对它的引用...所以我在这里创建了内存泄漏。看起来使内部类非静态并不能解决此问题-从getAuthTokenForActiveAccount()返回的引用仍将阻止外部Activity被垃圾回收。有没有任何方式可以在不泄漏上下文的情况下实现我想做的事情?
  2. Thread在其run()方法返回后就可以进行垃圾回收,对吧?但是在我的情况下,我希望这个线程保持存在,因为它也充当AccountManagerFuture,直到所有对它的引用都消失之前,它应该被保留在内存中。我的问题是:仅通过保持对Thread的(强)引用是否足以防止它被垃圾回收?如果不行,我该如何让这个Thread一直保持到所有引用消失为止?
2个回答

1

首先。使您的未来非静态将使其具有对其外部类(Activity)的隐式引用

  1. 您应该在您的未来和活动之间使用某种间接通信。无论如何,您应该将其移入服务中 - 您是否考虑过任何配置更改?您在哪里保存未来的引用? 我建议您将流程移入片段 - 然后您不必切换活动 - 并将未来放入保留的片段(以使其在方向更改时存活),或将其移到后台服务并通过广播接收器或事件总线与您的活动(或任何类型的UI)进行通信。

  2. 只要保留对它的某些引用,线程就不会被垃圾回收。无论它是否完成。我认为您正在将此与运行线程而没有保留对其引用的事实混淆。(我猜JVM确实这样做,但我必须承认我对此不确定)


感谢您对线程的澄清。至于第一部分 - 我不想重新实现“AccountManager”,因此我仍然要使用它的API(addAccount()getAuthToken()newChooseAccountIntent()等)- 这些API的一部分引用了我已经实现的“AccountAuthenticator”,而其中一些则负责切换活动,因此它们需要适当的上下文...是的,我可以重新发明轮子,但在这样做之前,我想确保这真的是我的唯一选择。 - Vasiliy
你所需要做的就是将你的Future或正在运行的后台操作从你的Activity中分离出来。这正是服务的用途所在。 - simekadam
@simekadam 不同意一种问题的解决方案。活动内存泄漏的根本原因是对活动的硬引用。即使lint建议基于将活动保持在弱引用中+声明类为静态的解决方案。 - x90
@x90 这取决于用例..当然弱引用可以防止您泄漏Activity。另一个问题是如何解析到您的新Activity的引用(在切换它们之后)。 - simekadam
各位,感谢你们的评论。我决定暂时恢复标准的 getAuthTokenByFeatures(),但我将来一定会再次研究这个问题,并且希望记得在这里发布我的发现。 - Vasiliy
显示剩余3条评论

0
问题1解决方案:
使用私有的WeakReference mContextHolder。当您需要上下文时,请调用mContextHolder.get()并检查是否为空;

问题2解决方案:
使用服务来托管您的线程。

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