リア充爆発日記

You don't even know what ria-ju really is.

Httpで画像をとってきてImageViewにセットする

Weak Referencesから学ぶJavaにおけるオブジェクトの4種類の参照でWeak Referenceを学んだ後、http://android-developers.blogspot.jp/2010/07/multithreading-for-performance.htmlを真似ながら、HTTPで画像をとってきてImageViewにセットするクラスを書いた。

テストはこんな感じ。

@RunWith(RobolectricTestRunner.class)
public class DownloadImageTaskTest {
    private static final MockWebServer mockWebServer = new MockWebServer();

    @BeforeClass
    public static void prepare() throws IOException {
        mockWebServer.play();
    }

    @AfterClass
    public static void cleanup() throws IOException {
        mockWebServer.shutdown();
    }

    @Test
    public void testTask() throws Exception {
        ByteArrayOutputStream buffer = createImageByteArray();

        mockWebServer.enqueue(new MockResponse()
            .setResponseCode(HttpRequest.SC_OK)
            .setBody(buffer.toByteArray()
            ));
        URL url = mockWebServer.getUrl("/");

        ImageView imageView = new ImageView(null);

        DownloadImageTask task = new DownloadImageTask(imageView);
        task.execute(url);

        assertThat(imageView.getDrawable(), is(notNullValue()));
    }

    private ByteArrayOutputStream createImageByteArray() throws IOException {
        File file = new File("./src/test/resources/files/jpg/猫.jpeg");
        InputStream in = new BufferedInputStream(new FileInputStream(file));
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();

        int nRead;
        byte[] data = new byte[1024];

        while ((nRead = in.read(data, 0, data.length)) != -1) {
            buffer.write(data, 0, nRead);
        }

        buffer.flush();
        return buffer;
    }

}


で、実装はこんな感じ。

public class DownloadImageTask extends AsyncTask<URL, Void, Bitmap> {
    private static final String TAG = DownloadImageTask.class.getSimpleName();

    private final WeakReference<ImageView> imageViewReference;

    public DownloadImageTask(ImageView imageView) {
        this.imageViewReference = new WeakReference<ImageView>(imageView);
    }

    @Override
    protected Bitmap doInBackground(URL... urls) {
        URL url = urls[0];

        try {
            HttpGet get = new HttpGet(url);
            HttpResponse response = get.execute();

            return response.getResponseBodyBitmap();

        } catch (MalformedURLException e) {
            Log.e(TAG, e.getMessage(), e); 
        } catch (IOException e) {
            Log.e(TAG, e.getMessage(), e); 
        }

        return null;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (imageViewReference != null) {
            ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }

        }
    }
}

doInBackground()内でViewに触ろうとするとdoInBackgroundの処理はUIスレッド上じゃないので

Only the original thread that created a view hierarchy can touch its views.

って言われるからね。
と言われて意味がよくわからなかったらAndroidの公式ドキュメントをちゃんと嫁、という啓示かもしれない。


ちなみにActivityからこんな感じで使う。

        new DownloadImageTask(mImageView).execute(imageUrl);


なお、

HttpGet get = new HttpGet(url);
HttpResponse response = get.execute();

は、どこかのと名前がかぶっているけど両方共お手製のクラスです。


今日は眠いなぁ。。