你并不一定要用弱引用来避免内存泄漏

原文:You don’t have to use WeakReference to avoid memory leaks 

16_12_13_0_16_34.jpeg

我的一个同事最近提到他们看的一个演讲,其中讲到:

如果你是一个安卓开发者却不使用弱引用,那么你就有麻烦了。

我个人认为这不仅是一种错误观点,而且相当误导人。WeakReference(弱引用)应该是修复内存泄漏的最后手段。

然后今天,我看到了Enrique López Mañas发布在Google Developers Experts专栏的一篇文章。

Finally understanding how references work in Android and Java
A few weeks ago I attended Mobiconf, one of the best conferences for Mobile Developers I had the pleasure to attend in…medium.com

这是一篇总结Java引用的好文章。

这篇文章并没有说必须使用弱引用,但是也没有给出替代的方案。我觉得我必须给出其它的方法来阐明弱引用并不是必须使用。

如果你不使用弱引用并不会真的有什么问题

我认为处处使用弱引用并不是一种最佳实践。使用弱引用来修复内存泄漏的问题往往意味着缺乏合理的架构。

虽然文章中给出的例子修复了潜在的内存泄漏问题,但是也有其它的方法。我可以给出两个耗时后台任务中避免内存泄漏的例子。

避免AsyncTask内存泄漏的简单例子:

Activity:

public class MainActivity extends Activity {
  private MyAsyncTask task;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    task = new MyAsyncTask();
    task.setListener(createListener());
    task.execute();
  }
  @Override
  protected void onDestroy() {
    task.setListener(null);
    super.onDestroy();
  }
  private MyAsyncTask.Listener createListener() {
    return new MyAsyncTask.Listener() {
      @Override
      public void onSuccess(Object object) {
        // adapt contents
      }
    };
  }
}

这里是AsyncTask:

class MyAsyncTask extends AsyncTask {
  private Listener listener;
  @Override
  protected Object doInBackground(Object\[\] params) {
    return doSomeStuff();
  }
  private Object doSomeStuff() {
    //do something to get result
    return new Object();
  }
  @Override
  protected void onPostExecute(Object object) {
    if (listener != null) {
      listener.onSuccess(object);
    }
  }
  public void setListener(Listener listener) {
    this.listener = listener;
  }
  interface Listener {
    void onSuccess(Object object);
  }
}

当然这个例子非常基础,但是我认为作为另一种解决方案的演示来说足够了。

这里是另一个使用RxJava实现的简单例子,我们仍然没有使用弱引用。

public class MainActivity extends Activity {
  private Subscription subscription;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    subscription = Observable
        .fromCallable(new Callable<Object>() {
          @Override
          public Object call() throws Exception {
            return doSomeStuff();
          }
        })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<Object>() {
          @Override
          public void call(Object o) {
            // adapt contents
          }
        });
  }
  private Object doSomeStuff() {
    //do something to get result
    return new Object();
  }
  @Override
  protected void onDestroy() {
    subscription.unsubscribe();
    super.onDestroy();
  }
}

注意如果我们没有unsubscribe Subscription那么仍然可能会出现内存泄漏。

最后我给出两个Novoda的项目,它们是很好的学习资源。你可能猜到了,它们并没有使用任何弱引用:)。

novoda/bonfire-firebase-sample
bonfire-firebase-sample - An app to discuss your favourite emojis. This is a sample app built with Firebase.github.com

novoda/spikes
spikes - Where ideas & concepts are born & incubatedgithub.com

我认为一个很重要的守则是让内部类为静态的。尤其是它们要做耗时的后台任务的时候。或者更好的方法是把这个类移到外面作为单独的类。

用非静态的内部类做耗时的后台任务总是很糟糕的实践,不光是在安卓中。