HttpClientクラスで「java.io.IOException: Attempted read from closed stream」が出るケース

2013-04-05T00:00:00+00:00 Android Java

多分、Androidに限った話じゃないと思うのですが

package sample.test;

import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;

public class SampleResponseHandler implements ResponseHandler<InputStream> {
    @Override
    public InputStream handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
        return response.getEntity().getContent();
    }
}

っていうのを作って

package sample.test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    @Override
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);

        new Thread() {
            @Override
            public void run() {
                InputStream is = null;

                try {
                    HttpClient httpClient = new DefaultHttpClient();

                    is = httpClient.execute(
                        new HttpGet("http://www.example.com/api/todo.json"),
                        new SampleResponseHandler()
                    );

                    BufferedReader br = null;

                    try {
                        br = new BufferedReader(new InputStreamReader(is));

                        String str;

                        while ((str = br.readLine()) != null) {
                            Log.v(TAG, str);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (br != null) {
                            br.close();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }.start();

    }
}

なんてやるケースとして、executeでInputStream等を取りたいとかする場合にはタイトルで書かれている例外が発生する。そういう場合はHttpEntityをBufferedHttpEntityでラップする事で解決出来るらしい、external/apache-http/src/org/apache/http/entity/BufferedHttpEntity.java見てみたけど一時的にストリームなバイト列辺りを取得しておいて、getContentを利用する際にByteArrayInputStreamを新しく作りなおして返すっていう感じな模様

なので上記に書いたSampleResponseHandlerを修正

package sample.test;

import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.entity.BufferedHttpEntity;

public class SampleResponseHandler implements ResponseHandler<InputStream> {
    @Override
    public InputStream handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
        return new BufferedHttpEntity(response.getEntity()).getContent();
    }
}

んまぁそういうケースもある模様なので。っていうか以前にも自分もハマった事があるんですが、要件がまったく違う(画像を取得する際にエラーが出る画像とそうじゃないのが存在したり等)

追記

そもそも普通にInputStreamを返すとなぜ例外が出るのかっていうのを調査

pirumを使ってPEARチャネルサーバーを構築 mocha+webdriverjs(+selenium)でテスト