【问题标题】:Android - RxJava vs AsyncTask to prevent getActivity() memory leakAndroid - RxJava vs AsyncTask 以防止 getActivity() 内存泄漏
【发布时间】:2015-04-04 05:25:57
【问题描述】:

在 Android 中使用 RxJava(或 RxAndroid 等)代替 AsyncTask 如何帮助防止上下文泄漏?在 AsyncTask 中,如果您执行它并且用户离开应用程序,则活动上下文可能为空,应用程序可能会崩溃。我听说 RxJava 在执行线程时可以帮助防止这种类型的崩溃。我还听说它可以比 AsyncTask 的 doInBackground 方法更好地处理错误(错误地处理错误)。大多数时候,如果出现任何故障,我只会在 doInBackground 中返回 null(例如),但我读过 RxJava 可以返回确切的错误并且不会泄漏。谁能举个例子?

这是一个关于 AsyncTask 崩溃的小演示,如果用户在尝试向 UI 报告结果时离开应用程序:

     @SuppressWarnings("unused")
private class GetTask extends AsyncTask<Void, Void, Void> {         

    @Override
    protected void onPostExecute(String result) {         
        pd = new ProgressDialog(getActivity().getApplicationContext());//can crash right here
        pd.setTitle("Grabbing Track!");
        pd.setMessage("Please wait...");
        pd.setCancelable(false);
        pd.setIndeterminate(true);
        pd.show();
    }}

这是一个 doInBackground 方法调用,它不会发出有用的错误:

@Override 
protected String doInBackground(String... params) {
    String myIntAsString = 1/0 + ""; //this should give an error (how do we report it to the caller??
                                  //or if we are parsing json and it fails, how do we report it to the caller cleanly. Can RxJava help?
}

【问题讨论】:

标签: android memory-leaks android-asynctask rx-java


【解决方案1】:

我认为 RxJava 的好处在于,如果您有一堆任务,您可以将它们按顺序排列,这样您就可以知道一个任务何时完成,下一个任务何时开始。在 AsyncTask 中,如果您有多个正在运行的任务,则无法保证哪个任务将首先完成,然后如果您关心订单,则必须进行大量错误检查。所以 RxJava 允许你对调用进行排序。

关于内存泄漏,我们可以将 AsyncTask 作为 Activity 的内部类。现在,由于当活动被销毁时它与活动相关联,所以上下文仍然存在并且不会被垃圾收集,这就是内存泄漏部分。

这就是 RxJava 可以提供帮助的地方。如果发生任何错误,那么我们可以调用订阅者 onError 方法。订阅者可能如下所示:

    public Observable<JsonObject> get_A_NetworkCall() {
    // Do your network call...but return an observable when done
}

Subscription subscription = get_A_NetworkCall()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<jsonResponse>() {

        @Override
        public void onCompleted() {
             // Update UI
        }

        @Override
        public void onError() {
             // show error on UI
        }

        @Override
        public void onNext(JsonObject response) {
             // Handle result of jsonResponse 
        }
});

或类似的东西 - 这是 psuedocode 。关键是您可以更干净地报告错误并在一行中切换线程。在这里,我们报告了 android 的主线程,但在新线程上进行了工作。在我们的活动 onDestroy 方法完成后,我们可以简单地取消订阅 observable,它会杀死它并防止我们遇到 AsyncTask 的任何内存泄漏。这对我来说应该是任何 asyncTasks 的替代品。

【讨论】:

  • 要添加...需要记住取消订阅 onPause() 或 onDestroy 如下 SubscriberObj.unsubscribe();
【解决方案2】:

我结合使用了两件事。首先,RxAndroid 真的很有帮助: https://github.com/ReactiveX/RxAndroid

你可以在它上面使用AppObservable.bindActivity来绑定一个observable,这样它的输出就会在主线程上被观察到,如果这个activity被调度销毁,消息将不会被转发。不过,您仍然必须管理暂停/恢复生命周期。为此,我使用这样的复合订阅(pseudojava 即将推出):

public class MyActivity extends Activity {
  private final CompositeSubscription subscriptions = new CompositeSubscription();

  @Override
  public void onResume() {
    super.onResume();
    subscriptions.add(AppObservable.bindActivity(this, myObservable)
        .subscribe());
    subscriptions.add(AppObservable.bindActivity(this, myOtherObservable)
        .subscribe());
  }

  @Override
  public void onPause() {
    subscriptions.clear();
    super.onPause();
  }

}

如果你想对数据做一些事情,显然你想在subscribe做更多的事情,但重要的是收集返回的Subscription实例并将它们添加到CompositeSubscription。当它清除它们时,它也会取消订阅它们。

使用这“两个奇怪的技巧”应该可以防止事情在 Activity 处于非操作状态时返回。

【讨论】: