【问题标题】:Retrofit callback on main thread主线程上的改造回调
【发布时间】:2014-03-06 07:45:59
【问题描述】:

通过这样的电话:

@GET("/user/{id}/data")
void getUserData(@Path("id") int id, Callback<Data> cb);

回调应该在主线程上执行(如果不使用 RxJava)。我的问题是:

  1. 解析发生在哪里(假设我使用 XML 转换器进行进程响应)。这是主线程,还是其他线程?它是否取决于转换器的实现?
  2. 如果我必须包含一些(繁重的)验证规则/业务规则,我是否需要在 callable 中生成一个新线程?还是可以在回调方法中完成?

我正在寻找从 Web 服务获取活动数据的方法,避免我自己进行线程管理(或使用 IntentService 等其他方法),但我也害怕使用 RxJava(因为实验支持)。有没有其他建议的方法来处理这个问题?

【问题讨论】:

  • "如果我必须包含一些(繁重的)验证规则/业务规则,我是否需要在 callable 中生成一个新线程?" -- 在这种情况下,您不应该使用 Callback。删除它,让getUserData() 返回Data,并在后台线程上调用getUserData(),例如doInBackground()AsyncTask。在那里,您可以在主应用程序线程上更新 UI 之前完成其余的工作。
  • @CommonsWare ;如果我删除回调,改造会进行同步 Web 服务调用,我将从活动中获得 NetworkOnMainthreadException。我拥有的另一个选择是从活动中的新线程进行改造 Web 服务调用 - 但正如我所提到的,我正在避免在我的代码中进行线程管理。我只是想知道 Retrofit 是否可以帮助我处理请求和响应,而无需使用线程/服务/处理程序/异步任务。谢谢!
  • “如果我删除回调,改造会进行同步 Web 服务调用,我会从活动中得到 NetworkOnMainthreadException”——如果你阅读了我的评论,我写了“调用 getUserData() 在后台线程”。 “我在我的代码中避免线程管理”——恕我直言,这不会被证明是一种有效的开发方法。使用第三方库来处理涉及线程的特定问题是可以的;说你拒绝自己处理线程是不切实际的。
  • @CommonsWare 啊..我明白了。我实际上正在研究更复杂的库,例如 Robospice(我知道它无法与 Retrofit 相比);但是 Robospice 让我的要求更高一些(因为它似乎并不依赖于我编写线程逻辑)。如果我从回调方法中生成线程来处理响应(而不是根本不使用回调),您是否发现任何问题?在最坏的情况下,我想避免活动中的线程,但可以(并且确实)在其他层中使用它,例如用于业务验证的“模型”。
  • “如果我从回调方法中生成线程来处理响应(而不是根本不使用回调),你会发现有什么问题吗?” -- 如果不是你需要处理的线程,那么告诉 Retrofit 在主应用程序线程上给你回电是绝对没有意义的。

标签: android retrofit


【解决方案1】:

解析发生在哪里(假设我使用 XML 转换器进行进程响应)。这是主线程,还是其他线程?它是否取决于转换器的实现?

无论您使用何种转换器,始终是后台线程。

如果我必须包含一些(繁重的)验证规则/业务规则,我是否需要在 callable 中生成一个新线程?还是可以在回调方法中完成?

这是相当主观的,有很多方法可以解决它。 Callback默认会在主线程上执行。

您可以通过向RestAdapter.Builder 提供自定义Executor 来更改将在其上调用回调的线程。这将影响由该RestAdapter 构建的所有服务,但是,这可能不是您想要的。

如果您想要完成的工作可以与更新 UI 并行完成(例如,轻量级缓存),那么从 Callback 生成另一个线程(或在执行程序上排队)没有任何问题。

如果必须在通知 UI 之前完成昂贵的工作,那么您最好将方法切换为同步(无回调)并自己进行线程处理。这样,您可以在 HTTP 调用之前和之后执行昂贵的操作(文件 i/o、缓存、转换、验证等)。

我们目前使用 RxJava(Retrofit 提供实验性支持)来满足您的要求:

interface Foo {
  @GET("/")
  Observable<Foo> getFoo(String bar);
}

foo.getFoo()
  .mapMany(new ExpensiveOperationFunction())
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Observer<TransformedFoo>() { .. });

【讨论】:

  • 非常感谢!我正在提供一个 Executor 来在不同的池中运行所有回调。完成后,我正在使用 EventBus 将其发布回主线程。现在一切似乎都很好,但我很好奇为什么我需要在 setExecutor() 方法(RestAdapter.Builder)中传递 2 个 Executor - 我可以为 httpExecutor 传递 null (假设我对默认值很好)。如果我为 httpExecutor 和 callbackExecutor 传递相同的执行程序怎么办?
  • 根据文档,为第二个(回调)执行程序传递 null 将在运行 HTTP 请求的同一线程上调用回调。您不能为 HTTP 执行程序传递 null。
  • Always a background thread no matter the converter you use.retrofit2 也是这样吗?
  • @gaara87 是的,转换器在 OkHttp 的工作线程上调用:github.com/square/retrofit/blob/…
猜你喜欢
  • 2014-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-17
  • 1970-01-01
相关资源
最近更新 更多