【问题标题】:Does Retrofit make network calls on main thread?Retrofit 是否在主线程上进行网络调用?
【发布时间】:2014-01-27 05:05:24
【问题描述】:

我正在尝试在 Android 上探索 Retrofit+OkHttp。这是我在网上找到的一些代码:

RestAdapter restAdapter = new RestAdapter.Builder().setExecutors(executor, executor)
.setClient(new OkClient(okHttpClient))
.setServer("blah").toString())
.build();

如果我不使用执行器服务,我的代码会在主线程上运行吗?因此,我应该在新线程中发出网络请求吗?

【问题讨论】:

标签: android retrofit okhttp


【解决方案1】:

可以为同步或异步执行声明改造方法。

具有返回类型的方法将被同步执行。

@GET("/user/{id}/photo")
Photo getUserPhoto(@Path("id") int id);

异步执行要求方法的最后一个参数是Callback

@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);

在 Android 上,回调将在主线程上执行。对于桌面应用程序,回调将发生在执行 HTTP 请求的同一线程上。

Retrofit 还集成了 RxJava 以支持返回类型为 rx.Observable 的方法

@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);

可观察的请求是异步订阅的,并在执行 HTTP 请求的同一线程上观察。要在不同的线程(例如 Android 的主线程)上观察,请在返回的 Observable 上调用 observeOn(Scheduler)

注意:RxJava 集成是实验性的。

【讨论】:

  • 感谢您的回答,我想知道为什么它被否决了!无论如何,我的问题是网络代码是否在幕后的单独线程(或 IntentService)上执行(就像 Volley 一样);还是我需要自己为此编写代码。您的响应针对的是响应处理...这又很有用:)
  • 正如答案中所述,如果您使用第二种模式(最后一个参数为Callback),则请求异步完成,但在主线程上调用回调。默认情况下,Retrofit 使用线程池处理这些请求。
  • 在 Retrofit 2 上,我认为我们使用 call.enqueue() 来获得异步响应。我的问题是如何调用不同的线程,从工作线程调用上面的getUserPhoto()method?
  • "注意:RxJava 集成是实验性的。"这仍然是真的吗?
  • 没有。那是近 2.5 年前的事了。
【解决方案2】:

返回值的方法是同步的。

@GET("/user/{id}/asset")
Asset getUserAsset(@Path("id") int id);

要做到这一点,您只需要添加一个回调即可。

@GET("/user/{id}/asset")
void getUserAsset(@Path("id") int id, Callback<Asset> cb);

希望对您有所帮助。

问候!

【讨论】:

  • 谢谢,我只是想确认一下 Retrofit 是否负责线程管理,因为你的库是为网络操作而设计的,网络操作应该在非 UI 线程上完成。
  • 你为什么不试试呢?我认为只运行代码是值得的,你会立即注意到它是否在主线程上被处理
  • 是的,很抱歉没有在我的最后评论中回复。调用是在主线程上进行的。
  • -1:这不是问题的答案。我觉得下面@Jake Wharton 的回答更适合这个问题。
【解决方案3】:

Kotlin 协程

当使用 suspend 函数发出网络请求时,Retrofit 使用协程 CallAdapter 在工作线程上调度。

因此,不需要在Dispatcher.IO 线程上显式启动协程。

请参阅 Android 文档:Page from network and database > Implement a RemoteMediator

示例

Injection.kt

object Injection {
    val feedService = Retrofit.Builder()
        .baseUrl(TWITTER_API_BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(FeedService::class.java)
}

FeedService.kt

interface FeedService {
    // Retrofit makes the request on a background thread.
    @GET("lists/{listType}")
    suspend fun getTweets(
        @Path(LIST_TYPE_PATH) listType: String,
        @Query(LIST_ID_QUERY) listId: String,
        @Query(LIST_COUNT_QUERY) count: String,
        @Query(LIST_PAGE_NUM_QUERY) page: String
    ): List<Tweet>
}

FeedPagingSource

class FeedPagingSource : PagingSource<Int, Tweet>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Tweet> {
        try {
            // The results are returned on the main thread.
            val tweets: List<Tweet> = Injection.feedService.getTweets(...)
            return LoadResult.Page(...)
        } catch (error: Exception) { ... }
    }
}

【讨论】:

    【解决方案4】:

    Retrofti 2

    大家可能都知道,Retrofit 可以同步和异步执行请求。

    同步方法在主线程上执行。这意味着在请求执行期间 UI 会阻塞,并且在此期间无法进行任何交互。

    对于非阻塞 UI,您必须自己在单独的线程中处理请求执行。这意味着,在等待响应时,您仍然可以与应用程序本身进行交互。

    如果你想同步执行请求,在 Retrofit2 中:

    同步请求

    public interface TaskService {  
      @GET("/tasks")
      Call<List<Task>> getTasks();
    }
    

    从同步请求中获取结果

    //service generator
    TaskService taskService = ServiceGenerator.createService(TaskService.class); 
    
    Call<List<Task>> call = taskService.getTasks();  
    List<Task>> tasks = call.execute().body();  
    

    但是如果你想异步执行请求:

    异步请求

    public interface TaskService {  
      @GET("/tasks")
      Call<List<Task>> getTasks();
    }
    

    从异步请求中获取结果

    //service generator    
    TaskService taskService = ServiceGenerator.createService(TaskService.class);  
    
    Call<List<Task>> call = taskService.getTasks();  
    call.enqueue(new Callback<List<Task>>() {  
      @Override
      public void onResponse(Call<List<Task>> call, Response<List<Task>> response) {
        if (response.isSuccessful()) {
            // tasks available
        } else {
            // error response, no access to resource?
        }
    }
    
     @Override
     public void onFailure(Call<List<Task>> call, Throwable t) {
        // something went completely south (like no internet connection)
        Log.d("Error", t.getMessage());
     }
    }
    

    如上所述:Retrofit 2 中的接口定义对于同步和异步请求是相同的。换句话说,在 Retrofit 2 中,每个请求都被包装到一个 Call 对象中。实际的同步或异步请求在稍后创建的调用对象上使用所需方法以不同方式执行。

    • 使用call.execute()同步运行。
    • 使用call.enqeue() 异步运行。

    【讨论】:

    • 谢谢。我正在使用改造 2 开发一个应用程序,并且想知道是否应该为网络 IO 创建线程,但现在我知道我不需要。
    猜你喜欢
    • 2013-02-06
    • 2020-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多