关于 async-http-client 中 SyncHttpClient 与 AsyncHttpClient 的问题求指点

2017-01-16 18:17:52 +08:00
 royliu

最近在学习网络加载库 async-http-client 有两个问题存在疑惑,恳请有研究过的 V 友指点一下:

  1. SyncHttpClient 为何要在子线程中使用?

    我已经知道 AsyncHttpClient 发异步请求是在sendRequest方法中,通过以下代码新建AsyncHttpRequest提交到线程池。

        AsyncHttpRequest request = newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context);
        threadPool.submit(request);
    

    而 SyncHttpClient 是继承自 AsyncHttpClient ,重写了sendRequest,但是在其方法中也调用了: newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context).run();

    我的疑惑:这里newAsyncHttpRequest返回的AsyncHttpRequest同样实现了Runnable并调用了run()方法,那么这不也是在子线程执行网络操作了。为什么我直接在 UI 线程调用

                   SyncHttpClient client = new SyncHttpClient();
                client.get(MainActivity.this, "http://www.baidu.com", new AsyncHttpResponseHandler() {
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
                    }
    
                    @Override
                    public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
    
                    }
                });
    

    为什么这里会报错:在主线程中执行了网络操作?

  2. SyncHttpClient 是在如何在线程中进行阻塞的?

    在一个子线程中进行两个请求,打印日志显示会等第一个请求结果返回才进行第二个请求,但是我查看其源码只找到一个responseHandler.setUseSynchronousMode(true);貌似有关系,但是具体是怎么进行阻塞的还不明白?

上面是我的两个疑惑,希望请各位大神指点一下,谢谢!

11620 次点击
所在节点    Android
9 条回复
linbiaye
2017-01-17 07:09:45 +08:00
1.为了保证用户体验,安卓规定不让在主线程中做网络 io ,主线程主要负责界面、交互。这个就是这么规定的,没有为什么。
2.感觉你现在不需要理解这个是怎么做到的,要理解这个怎么实现你需要知道 socket, tcp, http 是怎么回事,读取 socket(没有使用异步 io 下)就是阻塞的。 感觉你需要知道的是 sync 和 async 的区别?
royliu
2017-01-17 09:22:46 +08:00
@linbiaye 你好,感谢回复。
1.我知道 android 对主线程耗时操作不超过 5s 的规定,我只是想从源码的角度去理解时产生了疑惑。用 AsyncHttpClient 时是新建一个 AsyncHttpRequest 请求并 submit 到线程池里,所以相当于它本来就是运行在子线程中,在 UI 线程中也**不需要**
new Thread ( new Runable{
AsyncHttpClient client = new AsyncHttpClient();
client.get(....);
})
但是使用 SyncHttpClient (继承 AsyncHttpClient )发起一个请求时,也是 new 了一个 AsyncHttpRequest 并直接调用其 run 方法,在我看来这两个貌似只有有没有加到线程池里面的区别,但是为什么这里需要 new Thread 并在里面进行请求?

2. 我确实是想知道 sync 和 async 的区别,因为在我看来它们是使用相同的 AsyncHttpRequest 和 responseHandler ,我想知道是哪里的代码使得处理 AsyncHttpClient 请求异步而处理 SyncHttpClient 请求时阻塞呢?
naturs
2017-01-17 10:37:33 +08:00
AsyncHttpClient :
AsyncHttpRequest request = newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context);
threadPool.submit(request);

SyncHttpClient :
newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context).run();

一个用线程池 submit(),一个直接调用 run()方法,这就是区别。
linbiaye
2017-01-17 10:38:09 +08:00
1. 没明白你要表达啥。不管你是 sync 还是 async ,主线程都不让做 http 请求。你可以用线程池,也可以自己创建线程,反正别在我主线程做网络请求。
2.没读过,不知道具体实现
royliu
2017-01-17 11:02:32 +08:00
@linbiaye @naturs 被自己绕进去了,把 newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context).run(); 当做 new Thread ( newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context)).start()来理解了。感谢两位!
iluhcm
2017-01-17 12:37:11 +08:00
第二点,一个子线程中进行两个请求,其实就相当于在一个非 UI 的 Looper 中 run 了两个方法,所以结果当然是先执行完第一个,结果回来以后才会去执行第二个。

另外,你上面说的把 newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context).run(); 当做 new Thread(newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context).start()来理解是错误的。

直接调用 run()方法是在主线程执行的,调用 new Thread.start()方法是开启了一个新线程执行的,和调用 threadPool.submit(request)是一样的。
royliu
2017-01-17 13:39:52 +08:00
@iluhcm 是的,我昨天提出这个问题,就是因为理解错误,把 newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context).run();理解成了 new Thread(newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context).start()。一直在纠结明明这里新建了个线程,为什么在 UI 线程进行同步请求的时候还要 new Thread 。现在搞清楚了,感谢!
icris
2017-01-17 22:22:17 +08:00
https://github.com/AsyncHttpClient/async-http-client 这个吗?看起来不像 Android 用的东西,
>> It's built on top of Netty and currently requires JDK8.

还是推荐 Retrofit + RxJava 这一套,而且最好不要想着读它们的源码。
royliu
2017-01-17 22:57:11 +08:00
@icris 这个是在 apache 的 HttpClient 基础上为 Android 封装的网络请求库,前几年比较火,刚学 Android 的时候就是用的这个。现在项目中用的 Retrofit + RxJava ,确实很好用。以前只知道他们的用法并没有深究过内部的实现,现在想把这几个网络请求库源码都翻出来看看。
> 而且最好不要想着读它们的源码。
为啥不要想着读 Retrofit + RxJava 源码呢?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/334966

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX