ネットワークのレスポンスは com.android.volley.toolbox.BasicNetwork の performRequest() で処理されて、entity は entityToBytes() で一旦バイト配列に格納されます。
https://android.googlesource.com/platform/frameworks/volley/+/master/src/com/android/volley/toolbox/BasicNetwork.java
- @Override
- public NetworkResponse performRequest(Request<?> request) throws VolleyError {
- ...
- // Some responses such as 204s do not have content. We must check.
- if (httpResponse.getEntity() != null) {
- responseContents = entityToBytes(httpResponse.getEntity());
- } else {
- // Add 0 byte response as a way of honestly representing a
- // no-content request.
- responseContents = new byte[0];
- }
- ...
- }
- ...
- /** Reads the contents of HttpEntity into a byte[]. */
- private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
- PoolingByteArrayOutputStream bytes =
- new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
- byte[] buffer = null;
- try {
- InputStream in = entity.getContent();
- if (in == null) {
- throw new ServerError();
- }
- buffer = mPool.getBuf(1024);
- int count;
- while ((count = in.read(buffer)) != -1) {
- bytes.write(buffer, 0, count);
- }
- return bytes.toByteArray();
- } finally {
- try {
- // Close the InputStream and release the resources by "consuming the content".
- entity.consumeContent();
- } catch (IOException e) {
- // This can happen if there was an exception above that left the entity in
- // an invalid state.
- VolleyLog.v("Error occured when calling consumingContent");
- }
- mPool.returnBuf(buffer);
- bytes.close();
- }
- }
- public class PoolingByteArrayOutputStream extends ByteArrayOutputStream {
- ...
- /**
- * Ensures there is enough space in the buffer for the given number of additional bytes.
- */
- private void expand(int i) {
- /* Can the buffer handle @i more bytes, if not expand it */
- if (count + i <= buf.length) {
- return;
- }
- byte[] newbuf = mPool.getBuf((count + i) * 2);
- System.arraycopy(buf, 0, newbuf, 0, count);
- mPool.returnBuf(buf);
- buf = newbuf;
- }
- @Override
- public synchronized void write(byte[] buffer, int offset, int len) {
- expand(len);
- super.write(buffer, offset, len);
- }
- }
このように、(Bitmap化する際に縮小する場合でも)いったん元サイズのまま byte 配列に確保されるため、これを並列処理で行ったりすると OutOfMemory Error になることがあります(特に古いデバイスでは)。
Honeycomb (API Level 11) で AsyncTask の実行がシングルスレッドに戻ったのって、こういうメモリエラー回避のためなのかなとか思ったり思わなかったり。ちなみに AsyncTask は API Level 3 で追加されたのですが、追加されたときはシングルスレッドでの実行でした。スレッドプールによる並列処理になったのは Donut (API Level 4) からです。
「2.x のデバイス + Volley + 大きい画像 + AsyncTask」は危険!ということですね。
0 件のコメント:
コメントを投稿