ImageDownloadServiceで、大きな画像を非同期でダウンロード
AndroidでWebにある画像をダウンロードするサービスクラス ImageDownloadService を作りました。ソースはgithubで管理してます。
https://github.com/sharakova/ImageDownloadService
MIT Lisenceで、ご自由におつかいください。
説明
- DownloadManagerが利用できない、Android 2.1などで画像をダウンロードするプログラムです。
- 大きな画像をバックグランドでダウンロードします。
- ダウンロードした画像は、ギャラリーやコンテンツマネージャーでも表示されるようになります。
- ダウンロードを開始すると、Notificationでダウンロードの状態を確認できます。
- ダウンロードは、URLと保存したいタイトルをIntentで渡すだけです。
- 保存する画像は、他のアプリからも閲覧できる場所に保存されます。
- SDカードに画像を保存するため、SDカードが挿入されていない場合を想定してません。
- Notificationには、デフォルトのiconを設定していますが、他のICONも設定可能です。
- Serviceクラスを設定する jp.sharakova.android.service.ImageDownloadService
<service android:name="jp.sharakova.android.service.ImageDownloadService" />
- android.permission.INTERNET ネットを利用するためのパーミッション
- android.permission.WRITE_EXTERNAL_STORAGE SDカードを利用するためのパーミッション
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Activityから呼び出し
- Actionに、ImageDownloadService.IMAGE_DOWNLOAD_SERVICE を設定する。
- image_urlに、画像のURLを渡す
- image_titleには、画像のタイトルを設定する
Intent intent = new Intent(ImageDownloadService.IMAGE_DOWNLOAD_SERVICE); intent.setClass(getApplicationContext(), ImageDownloadService.class); intent.putExtra("image_url", "画像URL"); intent.putExtra("image_title", "画像の名前"); startService(intent);
ImageDownloadService.java
package jp.sharakova.android.service; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import jp.sharakova.android.imagedownload.R; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; import android.net.Uri; import android.os.Binder; import android.os.Environment; import android.os.IBinder; import android.provider.MediaStore.Images.Media; import android.util.Log; public class ImageDownloadService extends Service { public final static String IMAGE_DOWNLOAD_SERVICE = "jp.sharakova.android.service.ImageDownloadService"; private static ArrayList<String> downloadTask = new ArrayList<String>(); private NotificationManager mNM; private String imageUrl; private String imageTitle; private final IBinder mBinder = new LocalBinder(); public class LocalBinder extends Binder { ImageDownloadService getService() { return ImageDownloadService.this; } } @Override public void onCreate() { mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); // とりあえず、アクションもチェックする if (intent != null && IMAGE_DOWNLOAD_SERVICE.equals(intent.getAction())) { // URLとタイトルを設定する imageUrl = intent.getStringExtra("image_url"); imageTitle = intent.getStringExtra("image_title"); // ダウンロードタスクにaddする downloadTask.add(imageUrl); // ダウンロード開始するメッセージをタスクトレイで表示 showNotification(imageTitle, imageUrl); Thread downloadThread = new Thread(null, new Runnable() { public void run() { String path = imageUrl; String title = imageTitle; String message = null; try { // HTTPで画像をダウンロード DefaultHttpClient httpClient = new DefaultHttpClient(); HttpResponse response = httpClient.execute(new HttpGet(path)); InputStream stream = response.getEntity().getContent(); ContentResolver contentResolver = getContentResolver(); // ファイルへ保存 saveImage(contentResolver, getFileName(path), imageTitle, stream); // タスクトレイで表示するメッセージ message = title + " download complete."; // コンテンツマネージャーなどのアプリでは、キャッシュをクリアしてやらないと // すぐに、画像が表示されないので、メディアマウントをしてやる。 sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()))); } catch (Exception e) { message = "Error."; } finally { // ダウンロードのタスクから削除 downloadTask.remove(path); mNM.cancel(path.hashCode()); // メッセージをタスクトレイに表示 showNotification(message, path); } } }, "ImageDownloadService"); downloadThread.start(); } } @Override public void onDestroy() { mNM.cancelAll(); } @Override public IBinder onBind(Intent intent) { return mBinder; } private void showNotification(String message, String filePath) { Notification notification = new Notification(R.drawable.icon, message, System.currentTimeMillis()); PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, null, 0); notification.setLatestEventInfo(this, "download picture.", message, contentIntent); mNM.notify(filePath.hashCode(), notification); } private static String saveImage(ContentResolver contentResolver, String filename, String title, InputStream inputStream) { OutputStream outputStream = null; File file = null; try { makeDownloadDir(); file = new File(getDownloadPath(), filename); outputStream = new FileOutputStream(file); int DEFAULT_BUFFER_SIZE = 1024 * 4; byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int n = 0; while (-1 != (n = inputStream.read(buffer))) { outputStream.write(buffer, 0, n); } inputStream.close(); outputStream.close(); } catch (Exception e) { Log.w("save", e); if (file != null) { file.delete(); } } finally { if (outputStream != null) { try { outputStream.close(); } catch (Throwable t) { Log.w("save", "finally"); } } if (inputStream != null) { try { inputStream.close(); } catch (Throwable t) { Log.w("save", "finally"); } } } // ContentResolver にファイル情報の書き込み。 // これやらないと、ギャラリーなどのアプリでダウンロードした画像が表示されないです。 String filePath = getDownloadPath() + "/" + filename; ContentValues values = new ContentValues(7); values.put(Media.TITLE, title); values.put(Media.DISPLAY_NAME, filename); values.put(Media.DATE_TAKEN, System.currentTimeMillis()); values.put(Media.MIME_TYPE, getMimeType(filename)); values.put(Media.DATA, filePath); contentResolver.insert(Media.EXTERNAL_CONTENT_URI, values); return filePath; } private static String getSdCardPath() { return Environment.getExternalStorageDirectory().toString(); } private static String getDownloadPath() { // 別のフォルダを指定する場合は、下記のフォルダ名を変更してください。 return getSdCardPath() + "/download"; } private static void makeDownloadDir() { makeDir(new File(getDownloadPath())); } private static void makeDir(File dir) { if (!dir.exists()) { dir.mkdir(); } } private static String getFileName(String url) { int point = url.lastIndexOf("/"); if (point != -1) { return url.substring(point + 1, url.length()); } int hash = url.hashCode(); return String.valueOf(hash); } // mime-typeの判定が微妙です。すみません。 private static String getMimeType(String filename) { int point = filename.lastIndexOf("."); if (point != -1) { String extension = filename.substring(point + 1, filename.length()); if(extension.equals("jpeg") || extension.equals("jpg")) { return "image/jpeg"; } if(extension.equals("png")) { return "image/png"; } if(extension.equals("gif")) { return "image/gif"; } } return "image/jpeg"; } }
(実行サンプルソース)
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="jp.sharakova.android.imagedownload" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="7" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name="jp.sharakova.android.service.ImageDownloadSampleActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="jp.sharakova.android.service.ImageDownloadService" /> </application> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> </manifest>
ImageDownloadSampleActivity
package jp.sharakova.android.service; import jp.sharakova.android.imagedownload.R; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Toast; public class ImageDownloadSampleActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // ダウンロードのOnClickで、画像のダウンロードを開始する findViewById(R.id.download_btn).setOnClickListener(downloadBtnListener); } OnClickListener downloadBtnListener = new OnClickListener() { @Override public void onClick(View arg0) { Intent intent = new Intent(ImageDownloadService.IMAGE_DOWNLOAD_SERVICE); intent.setClass(getApplicationContext(), ImageDownloadService.class); intent.putExtra("image_url", "http://lh5.googleusercontent.com/-U_ZrYg86EMc/S9uxmpuT6hI/AAAAAAAACUs/-zOKvjJKH8E/s640/golf.png"); intent.putExtra("image_title", "golf"); startService(intent); // Toastの表示 Toast.makeText(getApplicationContext(), "download start", Toast.LENGTH_SHORT).show(); } }; }