読者です 読者をやめる 読者になる 読者になる

AndroidではHttpGet#abort()が効かないみたいな疑惑

android httpclient

OpenJDKでもOracleJDKでも、org.apache.http.client.methods.HttpGetのexecute()でblockしている間, 以下のように別Threadからabort()させることができます。

public class App {
  public static void main(String args[]) {
    final String hostname = "localhost";
    final HttpClient cli = new DefaultHttpClient();
    final HttpHost host = new HttpHost(hostname, 8080);
    final HttpGet req = new HttpGet("http://" + hostname);

    Runnable task = new Runnable() {
      public void run() {
        HttpResponse res;
        try {
          res = cli.execute(host, req);
          System.out.println("StatusLine => " + res.getStatusLine());
        } catch (ClientProtocolException e) {
          e.printStackTrace();
        } catch (IOException e) {
          e.printStackTrace();
        } finally {
          System.out.println("Finish");
        }
      }
    };
    ExecutorService es = Executors.newSingleThreadExecutor();
    es.execute(task);

    try {
      Thread.sleep(5 * 1000);
    } catch (InterruptedException e) {}

    req.abort();
    System.out.println("Abort");

    try {
      Thread.sleep(5 * 1000);
    } catch (InterruptedException e) {}
    es.shutdown();
  }
}

netcatでlistenするだけにしておいて, Javaのプログラムを起動させると...

komamitsu@carrot:~$ netcat -l 8080
GET / HTTP/1.1
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.2 (java 1.5)

以下のようなログを吐いて処理が終わる。

Abort
Nov 4, 2011 1:23:05 AM org.apache.http.impl.client.DefaultRequestDirector tryExecute
INFO: I/O exception (java.net.SocketException) caught when processing request: Socket closed
Finish
Nov 4, 2011 1:23:05 AM org.apache.http.impl.client.DefaultRequestDirector tryExecute
INFO: Retrying request
  :

ところがこれと同等のことをAndroidでやろうとすると...

public class HttpAndroidActivity extends Activity {
  protected static final String TAG = HttpAndroidActivity.class.getSimpleName();

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final String hostname = "10.0.2.2"; // Emulatorから見たHost PCのIP Address
    final HttpClient cli = new DefaultHttpClient();
    final HttpHost host = new HttpHost(hostname, 8080);
    final HttpGet req = new HttpGet("http://" + hostname);

    Runnable task = new Runnable() {
      public void run() {
        HttpResponse res;
        try {
          res = cli.execute(host, req);
          Log.i(TAG, "StatusLine => " + res.getStatusLine());
        } catch (ClientProtocolException e) {
          e.printStackTrace();
        } catch (IOException e) {
          e.printStackTrace();
        } finally {
          Log.i(TAG, "Finish");
        }
      }
    };
    ExecutorService es = Executors.newSingleThreadExecutor();
    es.execute(task);

    try {
      Thread.sleep(5 * 1000);
    } catch (InterruptedException e) {}

    req.abort();
    Log.i(TAG, "Abort");

    es.shutdown();
  }
}

HTTPのRequestがきた後

komamitsu@carrot:~$ netcat -l 8080
GET / HTTP/1.1
Host: 10.0.2.2:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)

abort()されているのに、全然execute()が中断されない...

I/ActivityManager(   58): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.komamitsu/.HttpAndroidActivity }
I/HttpAndroidActivity(  423): Abort
I/ActivityManager(   58): Displayed activity com.komamitsu/.HttpAndroidActivity: 5165 ms (total 5165 ms)

これはDalvikのコードを見ていく必要がありそうな気がするなぁ。



追記 (2011-11-04 01:50) :
AndroidのほうはOS2.2で試して上記の現象を確認したのだけど、2.3.3上では正常に動作する...

I/HttpAndroidActivity(  319): Abort
W/System.err(  319): java.net.SocketException: Socket closed
W/System.err(  319):    at org.apache.harmony.luni.platform.OSNetworkSystem.read(Native Method)
W/System.err(  319):    at dalvik.system.BlockGuard$WrappedNetworkSystem.read(BlockGuard.java:273)
W/System.err(  319):    at org.apache.harmony.luni.net.PlainSocketImpl.read(PlainSocketImpl.java:458)
W/System.err(  319):    at org.apache.harmony.luni.net.SocketInputStream.read(SocketInputStream.java:85)
W/System.err(  319):    at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:103)
W/System.err(  319):    at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:191)
W/System.err(  319):    at org.apache.http.impl.conn.DefaultResponseParser.parseHead(DefaultResponseParser.java:82)
W/System.err(  319):    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:174)
W/System.err(  319):    at org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader(AbstractHttpClientConnection.java:179)
W/System.err(  319):    at org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader(DefaultClientConnection.java:235)
W/System.err(  319):    at org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader(AbstractClientConnAdapter.java:259)
W/System.err(  319):    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:279)
W/System.err(  319):    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:121)
W/System.err(  319):    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:421)
W/System.err(  319):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
W/System.err(  319):    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:509)
W/System.err(  319):    at com.komamitsu.HttpAndroidActivity$1.run(HttpAndroidActivity.java:35)
W/System.err(  319):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
W/System.err(  319):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
W/System.err(  319):    at java.lang.Thread.run(Thread.java:1019)
I/HttpAndroidActivity(  319): Finish
I/ActivityManager(   61): Displayed com.komamitsu/.HttpAndroidActivity: +5s246ms