使用弱引用代替getActivity()(Android避免内存泄漏)?

12
为避免内存泄漏,我编写了以下方法,该方法将在活动和主要片段(使用继承)中使用。该方法应该允许我永远不直接调用活动来引用它。
为避免记忆泄漏,我编写了以下方法,将在活动和主要的片段(利用继承)中使用。该方法旨在使我永远不必通过调用activity直接引用它。
//this or getActivity()

这个方法是:

private WeakReference<BaseActivity> activityWeakReference = null; 

public BaseActivity getActivityFromWeakReference(){
        activityWeakReference = activityWeakReference == null ?
                new WeakReference<BaseActivity>((BaseActivity)getActivity()) :
                activityWeakReference;
        return activityWeakReference.get();
    }
调用 getActivityFromWeakReference() 方法而不是 getActivity() 方法是否安全,可以避免内存泄漏的威胁吗?
如果这样做不安全,我应该返回 activityWeakReference 并调用它的 get() 方法来使其安全吗?
我已经在多个片段中使用过它,到目前为止我还没有遇到任何问题。我提出这个问题是因为我在这里读到了这个(here):

只要助手的生命周期在活动的生命周期之内,那么就不需要使用 WeakReference。如果助手可以比 Activity 存活更长时间,那么当系统销毁它时,你应该使用 WeakReference 来避免在对象图中保留 Activity

到目前为止,我还没有遇到一个被引用的元素超过活动的情况。如果您发现错误或可能存在的错误,请在评论中写下。

1
请注意,当 activityWeakReference 不为 null,但 activityWeakReference.get() 为 null 时,您的方法可能返回 null。 - Mateus Gondim
@Mateus Gondim,感谢您的建议,我在使用之前已经检查了空值。 - Maxime Claude
2个回答

13

这完全是可行的。比如,你有这样一段伪代码:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

         new DownloadTask().execute();
    }

    public void showInfo() {
    }

    class DownloadTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            return null;
        }

        @Override
        protected void onPostExecute(Void data) {
            // we can call showInfo() activity because Asynctask hold an implicit reference to activity 
            showInfo();
        }
    }
}

在上面的代码中,存在一种可能会导致内存泄漏的情况。

以下是解释:

当您按照上面的示例创建DownloadTask时,Java将调用DownloadTask作为内部类。内部类将隐式持有对外部类的引用,在本例中是MainActivity。此外,当您启动异步任务时,该异步任务将由系统保留,直到它完成。例如,您的下载需要30秒。在这30秒内,您旋转了设备。当您旋转设备时,MainActivity重新创建,通常旧活动将被销毁。但在这种情况下,旧的MainActivity实例没有被销毁,因为旧MainActivity实例由DownloadTask持有,而DownloadTask又被系统保留。这将泄漏一个活动实例。

要修复此问题,您应该将上面的代码更改为:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new DownloadTask(this).execute();
    }

    public void showInfo() {
    }
}

class DownloadTask extends AsyncTask<Void, Void, Void> {
    WeakReference<MainActivity> mainActivityWeakReference;

    public DownloadTask(MainActivity activity) {
        mainActivityWeakReference = new WeakReference<MainActivity>(activity);
    }

    @Override
    protected Void doInBackground(Void... params) {
        return null;
    }

    @Override
    protected void onPostExecute(Void data) {
        if (mainActivityWeakReference.get() != null) {
            mainActivityWeakReference.get().showInfo();
        }
    }
}
在这种情况下,当创建新的MainActivity时,旧的不会被DownloadTask持有(由于弱引用属性),因此旧的将在未来被Android Garbage Collector销毁。每次使用弱引用对象时,您还应该检查,因为您无法确切知道GC何时会破坏这些对象。
这是我关于另一种内存泄漏情况的博客:在使用静态内部类时出现内存泄漏 希望这能帮到你。

我应该理解,如果您使用我上面的方法(getActivityFromWeakReference()),在您刚才描述的情况下将是可以的。 - Maxime Claude
2
@MaximeClaude 是的,基本上是这样。你需要在“某个地方”保留你的活动的WeakReference,并且你应该在你的方法中获取它(因为在某些情况下,它将为空,因为GC已经清理了它)。 “某个地方”取决于许多情况,我的例子只是其中之一。 - hqt
谢谢 :) 我会查看你的博客。 - Maxime Claude

1

有些情况下,如果您的片段被设置为保留实例,则它的生命周期会比活动更长,或者如果您的片段泄漏了。


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