またもお久しぶりです!
お盆10連休を満喫中のぱかぱかです。
本日は久しぶりにAndroidの記事になります。
近々社内のインターンで講師を担当することになり課題を予習していたのですが、意外と詰まるところがあり記事に残すことにしました!
本日のお題は「APIから返却された画像URLをもとに画像を表示する」というところになります!
作成したアプリの概要
今回は「お菓子の虜 Web API」を使ってお菓子の検索を行うアプリが題材です!
sysbird.jp
このAPI、iOS開発の本でも題材に取り上げられていたので有名なのでしょうか?
内容がお菓子なのでワクワクしてしまいますね…
今回作成したアプリの処理の流れとしては以下のようになっています。
①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(); } }); }
このメソッドをお菓子の詳細画面で呼んであげると、以下のように表示できました!
毎度のことながら、結果が目に見えると楽しいですね!
それでは今日はここら辺で!