V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
royliu
V2EX  ›  Android

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

  •  
  •   royliu · 2017-01-16 18:17:52 +08:00 · 12144 次点击
    这是一个创建于 2898 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在学习网络加载库 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);貌似有关系,但是具体是怎么进行阻塞的还不明白?

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

    9 条回复    2017-01-17 22:57:11 +08:00
    linbiaye
        1
    linbiaye  
       2017-01-17 07:09:45 +08:00
    1.为了保证用户体验,安卓规定不让在主线程中做网络 io ,主线程主要负责界面、交互。这个就是这么规定的,没有为什么。
    2.感觉你现在不需要理解这个是怎么做到的,要理解这个怎么实现你需要知道 socket, tcp, http 是怎么回事,读取 socket(没有使用异步 io 下)就是阻塞的。 感觉你需要知道的是 sync 和 async 的区别?
    royliu
        2
    royliu  
    OP
       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
        3
    naturs  
       2017-01-17 10:37:33 +08:00   ❤️ 1
    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
        4
    linbiaye  
       2017-01-17 10:38:09 +08:00   ❤️ 1
    1. 没明白你要表达啥。不管你是 sync 还是 async ,主线程都不让做 http 请求。你可以用线程池,也可以自己创建线程,反正别在我主线程做网络请求。
    2.没读过,不知道具体实现
    royliu
        5
    royliu  
    OP
       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
        6
    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
        7
    royliu  
    OP
       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
        8
    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
        9
    royliu  
    OP
       2017-01-17 22:57:11 +08:00
    @icris 这个是在 apache 的 HttpClient 基础上为 Android 封装的网络请求库,前几年比较火,刚学 Android 的时候就是用的这个。现在项目中用的 Retrofit + RxJava ,确实很好用。以前只知道他们的用法并没有深究过内部的实现,现在想把这几个网络请求库源码都翻出来看看。
    > 而且最好不要想着读它们的源码。
    为啥不要想着读 Retrofit + RxJava 源码呢?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3131 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 18ms · UTC 13:23 · PVG 21:23 · LAX 05:23 · JFK 08:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.