駆け出しエンジニアぱかぱかの成長記録

引くほど忘れっぽい新卒2年目駆け出しSEぱかぱかの備忘録です。

【Android】APIから返却されたURLから画像を表示

またもお久しぶりです!
お盆10連休を満喫中のぱかぱかです。

本日は久しぶりにAndroidの記事になります。
近々社内のインターンで講師を担当することになり課題を予習していたのですが、意外と詰まるところがあり記事に残すことにしました!

本日のお題は「APIから返却された画像URLをもとに画像を表示する」というところになります!

作成したアプリの概要

今回は「お菓子の虜 Web API」を使ってお菓子の検索を行うアプリが題材です!
sysbird.jp
このAPIiOS開発の本でも題材に取り上げられていたので有名なのでしょうか?
内容がお菓子なのでワクワクしてしまいますね…

今回作成したアプリの処理の流れとしては以下のようになっています。
①EditTextに入力した文字列からお菓子を検索し結果をListViewに表示
②任意のListをタップするとお菓子の詳細画面に遷移
③お菓子の詳細画面でAPIから返却されたURLをもとにお菓子の画像を表示 ←ココをやりたい

それでは早速みていきましょう!
あ、ちなみに今回は教材の関係でKotlinではなくJavaで書いてます!

画像取得の通信処理

お菓子を検索して結果がListViewに表示されるところまでは割愛します。
お菓子の画像はレスポンスの中でimageというフィールドに画像URLとして返却されてきます。

そのURLをもとに画像を取得する処理、これもHTTP通信処理になります。
通信処理は時間がかかるので、Androidでは別スレッドで行うことが強制されています。
メインスレッドで行うとNetworkOnMainThread例外が発生してしまいます。

◆非同期処理

この非同期処理、従来はAsyncTaskが使われていましたが、現在は非推奨となっています。
今回はjava.util.concurrentパッケージのExecutorを使って実装していきたいと思います!
(AsyncTaskを使用した方法も今度メモとして残そうと思っています。)

ちなみに以前Executor含めた非同期処理についてまとめた記事はこちら
(改めて見ると別にわかりやすくはないな…)
radish-se.hatenablog.com

今回は1枚の画像を取得するだけなので、単一のスレッドを準備し再利用するnewSingleThreadExecutorを使用します。

    protected void doInBackground(String imageUrl) {
        Executors.newSingleThreadExecutor().execute(() -> {

            // ここにRunnableインターフェースの実装を記述

    }

◆HTTP通信

記述の大まかな流れは以下の通り。
HttpURLConnectionクラスをURL#OpenConnectionから取得。
requestMethodプロパティでHTTPメソッドを宣言
InputStreamプロパティでサーバーからの応答にアクセス。

ここまではよくある流れだと思いますが、今回は画像なのでInputStreamをBitmapに変換する必要があるらしい?
Androidであまり画像を扱ったことがなくBitmapが割と初耳だった私…
あくまで対応している形式の一つがbmpということで、今回はそれに変換することで表示を行います。

 // HttpURLConnectionの取得
URL url = new URL(imageUrl);
HttpURLConnection urlCon = (HttpURLConnection) url.openConnection();

// タイムアウト設定
urlCon.setReadTimeout(10000);
urlCon.setConnectTimeout(20000);

// リクエストメソッド
urlCon.setRequestMethod("GET");

// サーバーの応答にアクセス
InputStream is = urlCon.getInputStream();

// Bitmapに変換
Bitmap bmp = BitmapFactory.decodeStream(is);

◆UI処理はメインスレッドで

ここで重要なのがUI処理はメインスレッドで行う必要があるという点です。
Handler/Looperという仕組みを利用します。

Handler:スレッド間でメッセージを受け渡す
Looper:別スレッドからのメッセージを管理し、順に処理する

今回は画像の取得とBitmapへの変換が終わり次第、UIでImageViewに画像を表示するという処理をメインスレッドで実行できるようにします。

// 処理結果をHandler経由でUIに反映
HandlerCompat.createAsync(getMainLooper()).post(() ->
        // Mainスレッドに渡す
        mImageView.setImageBitmap(bmp)
);

今回の実装まとめ

内容をまとめると以下のようになります。

    protected void doInBackground(String imageUrl) {
        Executors.newSingleThreadExecutor().execute(() -> {
            try {
                // HttpURLConnectionの取得
                URL url = new URL(imageUrl);
                HttpURLConnection urlCon = (HttpURLConnection) url.openConnection();

                // タイムアウト設定
                urlCon.setReadTimeout(10000);
                urlCon.setConnectTimeout(20000);

                // リクエストメソッド
                urlCon.setRequestMethod("GET");

                // サーバーの応答にアクセス
                InputStream is = urlCon.getInputStream();

                // Bitmapに変換
                Bitmap bmp = BitmapFactory.decodeStream(is);

                // 処理結果をHandler経由でUIに反映
                HandlerCompat.createAsync(getMainLooper()).post(() ->
                        // Mainスレッドに渡す
                        mImageView.setImageBitmap(bmp)
                );
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

このメソッドをお菓子の詳細画面で呼んであげると、以下のように表示できました!

美味しそう

毎度のことながら、結果が目に見えると楽しいですね!
それでは今日はここら辺で!