HTTP による画像ファイルのダウンロード
HTTP を利用してウェブサーバー上の画像ファイルを取得して、ImageView に表示する方法を紹介します。
あまり単純化しすぎず、ダウンロードする所のコードだけではなく、 実際に多くの場合に必要になるであろうサービスを利用した非同期のダウンロードとします。
これを理解するには、Java の基本的な I/O であるストリーム、 Android でのファイルの保存方法、 サービスの動作 (IntentService やアクティビティとの通信方法) 等を理解しておく必要があります。
HTTP で画像をダウンロードするサンプルプログラム
ここで作るプログラムは次のようなものです。
画面のように URL の入力ボックスとボタン、それと ImageView が配置されています。ImageView には初期値として Android アイコンが表示されています。
また、初期状態では見えていませんがボタンの下に進捗状況を表示するための TextView もおいてます。
ボタンをクリックするとダウンロードが始まり、ダウンロードが進むにつれてその進捗状況が表示されます。ダウンロードが完了すると、 下部の ImageView に画像が表示されます。
さて、こうしたプログラムをどのように作ったら良いか考えてみましょう。
プログラムの概要
プログラムの全体像はこうです。主な要素としては、アクティビティ、ブロードバンドレシーバ、ダウンロード用サービスの三つあります。
ボタンを押したとき、ダウンロード用サービスを開始します。このとき URL を渡します。
サービスは IntentService として作成して専用のスレッドで実行します。
ダウンロード java.net.HttpURLConnection を利用します。データは HttpURLConnection から取得できる InputStream として受けとります。 これをバイナリデータとしてそのままファイルに保存します。
ダウンロードの進捗状況は、ブロードキャストすることによって行います。この辺は 「ブロードキャストレシーバの実装によるサービスとアクティビティの通信」 を参考にしてください。
ソースコード
アクティビティは次のように実装しました。
package com.keicode.android.test;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
public class Download1Activity extends Activity
implements OnClickListener {
static String TAG = "Download1";
EditText urlEditText;
Button startButton;
TextView progressTextView;
ImageView imageView;
DownloadProgressBroadcastReceiver progressReceiver;
IntentFilter intentFilter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViews();
setListeners();
registerDownloadBroadcastReceiver();
}
protected void findViews(){
urlEditText = (EditText)findViewById(R.id.urlEditText);
startButton = (Button)findViewById(R.id.startButton);
progressTextView = (TextView)findViewById(R.id.progressTextView);
imageView = (ImageView)findViewById(R.id.imageView1);
}
protected void setListeners(){
startButton.setOnClickListener(this);
}
protected void registerDownloadBroadcastReceiver(){
progressReceiver = new DownloadProgressBroadcastReceiver();
intentFilter = new IntentFilter();
intentFilter.addAction("DOWNLOAD_PROGRESS_ACTION");
registerReceiver(progressReceiver, intentFilter);
}
protected void startDownload(){
Intent intent = new Intent(getBaseContext(), DownloadService.class);
intent.putExtra("url", urlEditText.getText().toString());
startService(intent);
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.startButton:
startDownload();
break;
}
}
class DownloadProgressBroadcastReceiver
extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Show Progress
Bundle bundle = intent.getExtras();
int completePercent = bundle.getInt("completePercent");
int totalByte = bundle.getInt("totalByte");
String progressString = totalByte + " byte read.";
if(0 < completePercent){
progressString += "[" + completePercent + "%]";
}
progressTextView.setText(progressString);
// If completed, show the picture.
if(completePercent == 100){
String fileName = bundle.getString("filename");
Bitmap bitmap = BitmapFactory.decodeFile(
"/data/data/com.keicode.android.test/files/"
+ fileName);
if(bitmap != null){
imageView.setImageBitmap(bitmap);
}
}
}
}
}
ブロードキャストレシーバも内部クラスとして簡単に実装してある所に注意してください。
サービス側は次の通りです。
package com.keicode.android.test;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import org.apache.http.HttpException;
import android.app.IntentService;
import android.content.*;
import android.os.Bundle;
import android.util.Log;
public class DownloadService extends IntentService {
static final String TAG = "Download1";
public DownloadService() {
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
try {
Bundle bundle = intent.getExtras();
if(bundle == null){
Log.d(TAG, "bundle == null");
return;
}
String urlString = bundle.getString("url");
// HTTP Connection
URL url = new URL(urlString);
String fileName = getFilenameFromURL(url);
Log.d(TAG, fileName);
URLConnection conn = url.openConnection();
HttpURLConnection httpConn = (HttpURLConnection)conn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.connect();
int response = httpConn.getResponseCode();
// Check Response
if(response != HttpURLConnection.HTTP_OK){
throw new HttpException();
}
int contentLength = httpConn.getContentLength();
InputStream in = httpConn.getInputStream();
FileOutputStream outStream
= openFileOutput(fileName, MODE_PRIVATE);
DataInputStream dataInStream = new DataInputStream(in);
DataOutputStream dataOutStream
= new DataOutputStream(
new BufferedOutputStream(outStream));
// Read Data
byte[] b= new byte[4096];
int readByte = 0, totalByte = 0;
while(-1 != (readByte = dataInStream.read(b))){
dataOutStream.write(b, 0, readByte);
totalByte += readByte;
sendProgressBroadcast(
contentLength,
totalByte,
fileName);
}
dataInStream.close();
dataOutStream.close();
if(contentLength < 0){
sendProgressBroadcast(
totalByte,
totalByte,
fileName);
}
} catch (IOException e) {
Log.d(TAG, "IOException");
} catch (HttpException e) {
Log.d(TAG, "HttpException");
}
}
protected void sendProgressBroadcast(
int contentLength,
int totalByte,
String filename){
Intent broadcastIntent = new Intent();
int completePercent = contentLength < 0 ?
-1 : ((totalByte*1000)/(contentLength*10));
Log.d(TAG, "completePercent = " + completePercent);
Log.d(TAG, "totalByte = " + totalByte);
Log.d(TAG, "fileName = " + filename);
broadcastIntent.putExtra("completePercent", completePercent);
broadcastIntent.putExtra("totalByte", totalByte);
broadcastIntent.putExtra("filename", filename);
broadcastIntent.setAction("DOWNLOAD_PROGRESS_ACTION");
getBaseContext().sendBroadcast(broadcastIntent);
}
protected String getFilenameFromURL(URL url){
String[] p = url.getFile().split("/");
String s = p[p.length-1];
if(s.indexOf("?") > -1){
return s.substring(0, s.indexOf("?"));
}
return s;
}
}
エラー処理、例外処理はほとんど何もしてません。ネットワーク系のプログラムではいろんな場所でいろんなエラーがでるのが常なので、 本来はちゃんと処理しないといけないんですけどね。