HttpClientクラスで「java.io.IOException: Attempted read from closed stream」が出るケース
多分、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を返すとなぜ例外が出るのかっていうのを調査
確証って訳じゃないけど、IOExceptionが出る原因ってHttpEntityの実装によってはconsumeContentでInputStreamがcloseされてる模様。だからその後でreadしたりしようとしても例外になるっていうオチか
— kinjouj (@kinjou__j) April 5, 2013
でBufferedHttpEntityとかはHttpEntityWrapperを継承しているけど、こちらでもconsumeContentは呼ばれてInputStream.closeはされちゃうがその前にEntityUtils.toByteArrayでバッファを確保しておくからだと
— kinjouj (@kinjou__j) April 5, 2013
でその確保されたバッファ(byte[])をgetContentが呼ばれる際に新しくByteArrayInputStreamを作りなおす。的な感じなのかなと
— kinjouj (@kinjou__j) April 5, 2013